9c24216439dc3e1820999970bdc5ca450ad9d9dc
[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         // if passed an array or multiple args do them one by one
2555         if(multi){
2556             for(var i = 0, len = multi.length; i < len; i++) {
2557                 this.appendChild(multi[i]);
2558             }
2559         }else{
2560             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
2561                 return false;
2562             }
2563             var index = this.childNodes.length;
2564             var oldParent = node.parentNode;
2565             // it's a move, make sure we move it cleanly
2566             if(oldParent){
2567                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
2568                     return false;
2569                 }
2570                 oldParent.removeChild(node);
2571             }
2572             index = this.childNodes.length;
2573             if(index == 0){
2574                 this.setFirstChild(node);
2575             }
2576             this.childNodes.push(node);
2577             node.parentNode = this;
2578             var ps = this.childNodes[index-1];
2579             if(ps){
2580                 node.previousSibling = ps;
2581                 ps.nextSibling = node;
2582             }else{
2583                 node.previousSibling = null;
2584             }
2585             node.nextSibling = null;
2586             this.setLastChild(node);
2587             node.setOwnerTree(this.getOwnerTree());
2588             this.fireEvent("append", this.ownerTree, this, node, index);
2589             if(oldParent){
2590                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
2591             }
2592             return node;
2593         }
2594     },
2595
2596     /**
2597      * Removes a child node from this node.
2598      * @param {Node} node The node to remove
2599      * @return {Node} The removed node
2600      */
2601     removeChild : function(node){
2602         var index = this.childNodes.indexOf(node);
2603         if(index == -1){
2604             return false;
2605         }
2606         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
2607             return false;
2608         }
2609
2610         // remove it from childNodes collection
2611         this.childNodes.splice(index, 1);
2612
2613         // update siblings
2614         if(node.previousSibling){
2615             node.previousSibling.nextSibling = node.nextSibling;
2616         }
2617         if(node.nextSibling){
2618             node.nextSibling.previousSibling = node.previousSibling;
2619         }
2620
2621         // update child refs
2622         if(this.firstChild == node){
2623             this.setFirstChild(node.nextSibling);
2624         }
2625         if(this.lastChild == node){
2626             this.setLastChild(node.previousSibling);
2627         }
2628
2629         node.setOwnerTree(null);
2630         // clear any references from the node
2631         node.parentNode = null;
2632         node.previousSibling = null;
2633         node.nextSibling = null;
2634         this.fireEvent("remove", this.ownerTree, this, node);
2635         return node;
2636     },
2637
2638     /**
2639      * Inserts the first node before the second node in this nodes childNodes collection.
2640      * @param {Node} node The node to insert
2641      * @param {Node} refNode The node to insert before (if null the node is appended)
2642      * @return {Node} The inserted node
2643      */
2644     insertBefore : function(node, refNode){
2645         if(!refNode){ // like standard Dom, refNode can be null for append
2646             return this.appendChild(node);
2647         }
2648         // nothing to do
2649         if(node == refNode){
2650             return false;
2651         }
2652
2653         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
2654             return false;
2655         }
2656         var index = this.childNodes.indexOf(refNode);
2657         var oldParent = node.parentNode;
2658         var refIndex = index;
2659
2660         // when moving internally, indexes will change after remove
2661         if(oldParent == this && this.childNodes.indexOf(node) < index){
2662             refIndex--;
2663         }
2664
2665         // it's a move, make sure we move it cleanly
2666         if(oldParent){
2667             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
2668                 return false;
2669             }
2670             oldParent.removeChild(node);
2671         }
2672         if(refIndex == 0){
2673             this.setFirstChild(node);
2674         }
2675         this.childNodes.splice(refIndex, 0, node);
2676         node.parentNode = this;
2677         var ps = this.childNodes[refIndex-1];
2678         if(ps){
2679             node.previousSibling = ps;
2680             ps.nextSibling = node;
2681         }else{
2682             node.previousSibling = null;
2683         }
2684         node.nextSibling = refNode;
2685         refNode.previousSibling = node;
2686         node.setOwnerTree(this.getOwnerTree());
2687         this.fireEvent("insert", this.ownerTree, this, node, refNode);
2688         if(oldParent){
2689             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
2690         }
2691         return node;
2692     },
2693
2694     /**
2695      * Returns the child node at the specified index.
2696      * @param {Number} index
2697      * @return {Node}
2698      */
2699     item : function(index){
2700         return this.childNodes[index];
2701     },
2702
2703     /**
2704      * Replaces one child node in this node with another.
2705      * @param {Node} newChild The replacement node
2706      * @param {Node} oldChild The node to replace
2707      * @return {Node} The replaced node
2708      */
2709     replaceChild : function(newChild, oldChild){
2710         this.insertBefore(newChild, oldChild);
2711         this.removeChild(oldChild);
2712         return oldChild;
2713     },
2714
2715     /**
2716      * Returns the index of a child node
2717      * @param {Node} node
2718      * @return {Number} The index of the node or -1 if it was not found
2719      */
2720     indexOf : function(child){
2721         return this.childNodes.indexOf(child);
2722     },
2723
2724     /**
2725      * Returns the tree this node is in.
2726      * @return {Tree}
2727      */
2728     getOwnerTree : function(){
2729         // if it doesn't have one, look for one
2730         if(!this.ownerTree){
2731             var p = this;
2732             while(p){
2733                 if(p.ownerTree){
2734                     this.ownerTree = p.ownerTree;
2735                     break;
2736                 }
2737                 p = p.parentNode;
2738             }
2739         }
2740         return this.ownerTree;
2741     },
2742
2743     /**
2744      * Returns depth of this node (the root node has a depth of 0)
2745      * @return {Number}
2746      */
2747     getDepth : function(){
2748         var depth = 0;
2749         var p = this;
2750         while(p.parentNode){
2751             ++depth;
2752             p = p.parentNode;
2753         }
2754         return depth;
2755     },
2756
2757     // private
2758     setOwnerTree : function(tree){
2759         // if it's move, we need to update everyone
2760         if(tree != this.ownerTree){
2761             if(this.ownerTree){
2762                 this.ownerTree.unregisterNode(this);
2763             }
2764             this.ownerTree = tree;
2765             var cs = this.childNodes;
2766             for(var i = 0, len = cs.length; i < len; i++) {
2767                 cs[i].setOwnerTree(tree);
2768             }
2769             if(tree){
2770                 tree.registerNode(this);
2771             }
2772         }
2773     },
2774
2775     /**
2776      * Returns the path for this node. The path can be used to expand or select this node programmatically.
2777      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
2778      * @return {String} The path
2779      */
2780     getPath : function(attr){
2781         attr = attr || "id";
2782         var p = this.parentNode;
2783         var b = [this.attributes[attr]];
2784         while(p){
2785             b.unshift(p.attributes[attr]);
2786             p = p.parentNode;
2787         }
2788         var sep = this.getOwnerTree().pathSeparator;
2789         return sep + b.join(sep);
2790     },
2791
2792     /**
2793      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2794      * function call will be the scope provided or the current node. The arguments to the function
2795      * will be the args provided or the current node. If the function returns false at any point,
2796      * the bubble is stopped.
2797      * @param {Function} fn The function to call
2798      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2799      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2800      */
2801     bubble : function(fn, scope, args){
2802         var p = this;
2803         while(p){
2804             if(fn.call(scope || p, args || p) === false){
2805                 break;
2806             }
2807             p = p.parentNode;
2808         }
2809     },
2810
2811     /**
2812      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2813      * function call will be the scope provided or the current node. The arguments to the function
2814      * will be the args provided or the current node. If the function returns false at any point,
2815      * the cascade is stopped on that branch.
2816      * @param {Function} fn The function to call
2817      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2818      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2819      */
2820     cascade : function(fn, scope, args){
2821         if(fn.call(scope || this, args || this) !== false){
2822             var cs = this.childNodes;
2823             for(var i = 0, len = cs.length; i < len; i++) {
2824                 cs[i].cascade(fn, scope, args);
2825             }
2826         }
2827     },
2828
2829     /**
2830      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
2831      * function call will be the scope provided or the current node. The arguments to the function
2832      * will be the args provided or the current node. If the function returns false at any point,
2833      * the iteration stops.
2834      * @param {Function} fn The function to call
2835      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2836      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2837      */
2838     eachChild : function(fn, scope, args){
2839         var cs = this.childNodes;
2840         for(var i = 0, len = cs.length; i < len; i++) {
2841                 if(fn.call(scope || this, args || cs[i]) === false){
2842                     break;
2843                 }
2844         }
2845     },
2846
2847     /**
2848      * Finds the first child that has the attribute with the specified value.
2849      * @param {String} attribute The attribute name
2850      * @param {Mixed} value The value to search for
2851      * @return {Node} The found child or null if none was found
2852      */
2853     findChild : function(attribute, value){
2854         var cs = this.childNodes;
2855         for(var i = 0, len = cs.length; i < len; i++) {
2856                 if(cs[i].attributes[attribute] == value){
2857                     return cs[i];
2858                 }
2859         }
2860         return null;
2861     },
2862
2863     /**
2864      * Finds the first child by a custom function. The child matches if the function passed
2865      * returns true.
2866      * @param {Function} fn
2867      * @param {Object} scope (optional)
2868      * @return {Node} The found child or null if none was found
2869      */
2870     findChildBy : function(fn, scope){
2871         var cs = this.childNodes;
2872         for(var i = 0, len = cs.length; i < len; i++) {
2873                 if(fn.call(scope||cs[i], cs[i]) === true){
2874                     return cs[i];
2875                 }
2876         }
2877         return null;
2878     },
2879
2880     /**
2881      * Sorts this nodes children using the supplied sort function
2882      * @param {Function} fn
2883      * @param {Object} scope (optional)
2884      */
2885     sort : function(fn, scope){
2886         var cs = this.childNodes;
2887         var len = cs.length;
2888         if(len > 0){
2889             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
2890             cs.sort(sortFn);
2891             for(var i = 0; i < len; i++){
2892                 var n = cs[i];
2893                 n.previousSibling = cs[i-1];
2894                 n.nextSibling = cs[i+1];
2895                 if(i == 0){
2896                     this.setFirstChild(n);
2897                 }
2898                 if(i == len-1){
2899                     this.setLastChild(n);
2900                 }
2901             }
2902         }
2903     },
2904
2905     /**
2906      * Returns true if this node is an ancestor (at any point) of the passed node.
2907      * @param {Node} node
2908      * @return {Boolean}
2909      */
2910     contains : function(node){
2911         return node.isAncestor(this);
2912     },
2913
2914     /**
2915      * Returns true if the passed node is an ancestor (at any point) of this node.
2916      * @param {Node} node
2917      * @return {Boolean}
2918      */
2919     isAncestor : function(node){
2920         var p = this.parentNode;
2921         while(p){
2922             if(p == node){
2923                 return true;
2924             }
2925             p = p.parentNode;
2926         }
2927         return false;
2928     },
2929
2930     toString : function(){
2931         return "[Node"+(this.id?" "+this.id:"")+"]";
2932     }
2933 });/*
2934  * Based on:
2935  * Ext JS Library 1.1.1
2936  * Copyright(c) 2006-2007, Ext JS, LLC.
2937  *
2938  * Originally Released Under LGPL - original licence link has changed is not relivant.
2939  *
2940  * Fork - LGPL
2941  * <script type="text/javascript">
2942  */
2943  (function(){ 
2944 /**
2945  * @class Roo.Layer
2946  * @extends Roo.Element
2947  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
2948  * automatic maintaining of shadow/shim positions.
2949  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
2950  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
2951  * you can pass a string with a CSS class name. False turns off the shadow.
2952  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
2953  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
2954  * @cfg {String} cls CSS class to add to the element
2955  * @cfg {Number} zindex Starting z-index (defaults to 11000)
2956  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
2957  * @constructor
2958  * @param {Object} config An object with config options.
2959  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
2960  */
2961
2962 Roo.Layer = function(config, existingEl){
2963     config = config || {};
2964     var dh = Roo.DomHelper;
2965     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
2966     if(existingEl){
2967         this.dom = Roo.getDom(existingEl);
2968     }
2969     if(!this.dom){
2970         var o = config.dh || {tag: "div", cls: "x-layer"};
2971         this.dom = dh.append(pel, o);
2972     }
2973     if(config.cls){
2974         this.addClass(config.cls);
2975     }
2976     this.constrain = config.constrain !== false;
2977     this.visibilityMode = Roo.Element.VISIBILITY;
2978     if(config.id){
2979         this.id = this.dom.id = config.id;
2980     }else{
2981         this.id = Roo.id(this.dom);
2982     }
2983     this.zindex = config.zindex || this.getZIndex();
2984     this.position("absolute", this.zindex);
2985     if(config.shadow){
2986         this.shadowOffset = config.shadowOffset || 4;
2987         this.shadow = new Roo.Shadow({
2988             offset : this.shadowOffset,
2989             mode : config.shadow
2990         });
2991     }else{
2992         this.shadowOffset = 0;
2993     }
2994     this.useShim = config.shim !== false && Roo.useShims;
2995     this.useDisplay = config.useDisplay;
2996     this.hide();
2997 };
2998
2999 var supr = Roo.Element.prototype;
3000
3001 // shims are shared among layer to keep from having 100 iframes
3002 var shims = [];
3003
3004 Roo.extend(Roo.Layer, Roo.Element, {
3005
3006     getZIndex : function(){
3007         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
3008     },
3009
3010     getShim : function(){
3011         if(!this.useShim){
3012             return null;
3013         }
3014         if(this.shim){
3015             return this.shim;
3016         }
3017         var shim = shims.shift();
3018         if(!shim){
3019             shim = this.createShim();
3020             shim.enableDisplayMode('block');
3021             shim.dom.style.display = 'none';
3022             shim.dom.style.visibility = 'visible';
3023         }
3024         var pn = this.dom.parentNode;
3025         if(shim.dom.parentNode != pn){
3026             pn.insertBefore(shim.dom, this.dom);
3027         }
3028         shim.setStyle('z-index', this.getZIndex()-2);
3029         this.shim = shim;
3030         return shim;
3031     },
3032
3033     hideShim : function(){
3034         if(this.shim){
3035             this.shim.setDisplayed(false);
3036             shims.push(this.shim);
3037             delete this.shim;
3038         }
3039     },
3040
3041     disableShadow : function(){
3042         if(this.shadow){
3043             this.shadowDisabled = true;
3044             this.shadow.hide();
3045             this.lastShadowOffset = this.shadowOffset;
3046             this.shadowOffset = 0;
3047         }
3048     },
3049
3050     enableShadow : function(show){
3051         if(this.shadow){
3052             this.shadowDisabled = false;
3053             this.shadowOffset = this.lastShadowOffset;
3054             delete this.lastShadowOffset;
3055             if(show){
3056                 this.sync(true);
3057             }
3058         }
3059     },
3060
3061     // private
3062     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
3063     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
3064     sync : function(doShow){
3065         var sw = this.shadow;
3066         if(!this.updating && this.isVisible() && (sw || this.useShim)){
3067             var sh = this.getShim();
3068
3069             var w = this.getWidth(),
3070                 h = this.getHeight();
3071
3072             var l = this.getLeft(true),
3073                 t = this.getTop(true);
3074
3075             if(sw && !this.shadowDisabled){
3076                 if(doShow && !sw.isVisible()){
3077                     sw.show(this);
3078                 }else{
3079                     sw.realign(l, t, w, h);
3080                 }
3081                 if(sh){
3082                     if(doShow){
3083                        sh.show();
3084                     }
3085                     // fit the shim behind the shadow, so it is shimmed too
3086                     var a = sw.adjusts, s = sh.dom.style;
3087                     s.left = (Math.min(l, l+a.l))+"px";
3088                     s.top = (Math.min(t, t+a.t))+"px";
3089                     s.width = (w+a.w)+"px";
3090                     s.height = (h+a.h)+"px";
3091                 }
3092             }else if(sh){
3093                 if(doShow){
3094                    sh.show();
3095                 }
3096                 sh.setSize(w, h);
3097                 sh.setLeftTop(l, t);
3098             }
3099             
3100         }
3101     },
3102
3103     // private
3104     destroy : function(){
3105         this.hideShim();
3106         if(this.shadow){
3107             this.shadow.hide();
3108         }
3109         this.removeAllListeners();
3110         var pn = this.dom.parentNode;
3111         if(pn){
3112             pn.removeChild(this.dom);
3113         }
3114         Roo.Element.uncache(this.id);
3115     },
3116
3117     remove : function(){
3118         this.destroy();
3119     },
3120
3121     // private
3122     beginUpdate : function(){
3123         this.updating = true;
3124     },
3125
3126     // private
3127     endUpdate : function(){
3128         this.updating = false;
3129         this.sync(true);
3130     },
3131
3132     // private
3133     hideUnders : function(negOffset){
3134         if(this.shadow){
3135             this.shadow.hide();
3136         }
3137         this.hideShim();
3138     },
3139
3140     // private
3141     constrainXY : function(){
3142         if(this.constrain){
3143             var vw = Roo.lib.Dom.getViewWidth(),
3144                 vh = Roo.lib.Dom.getViewHeight();
3145             var s = Roo.get(document).getScroll();
3146
3147             var xy = this.getXY();
3148             var x = xy[0], y = xy[1];   
3149             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
3150             // only move it if it needs it
3151             var moved = false;
3152             // first validate right/bottom
3153             if((x + w) > vw+s.left){
3154                 x = vw - w - this.shadowOffset;
3155                 moved = true;
3156             }
3157             if((y + h) > vh+s.top){
3158                 y = vh - h - this.shadowOffset;
3159                 moved = true;
3160             }
3161             // then make sure top/left isn't negative
3162             if(x < s.left){
3163                 x = s.left;
3164                 moved = true;
3165             }
3166             if(y < s.top){
3167                 y = s.top;
3168                 moved = true;
3169             }
3170             if(moved){
3171                 if(this.avoidY){
3172                     var ay = this.avoidY;
3173                     if(y <= ay && (y+h) >= ay){
3174                         y = ay-h-5;   
3175                     }
3176                 }
3177                 xy = [x, y];
3178                 this.storeXY(xy);
3179                 supr.setXY.call(this, xy);
3180                 this.sync();
3181             }
3182         }
3183     },
3184
3185     isVisible : function(){
3186         return this.visible;    
3187     },
3188
3189     // private
3190     showAction : function(){
3191         this.visible = true; // track visibility to prevent getStyle calls
3192         if(this.useDisplay === true){
3193             this.setDisplayed("");
3194         }else if(this.lastXY){
3195             supr.setXY.call(this, this.lastXY);
3196         }else if(this.lastLT){
3197             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
3198         }
3199     },
3200
3201     // private
3202     hideAction : function(){
3203         this.visible = false;
3204         if(this.useDisplay === true){
3205             this.setDisplayed(false);
3206         }else{
3207             this.setLeftTop(-10000,-10000);
3208         }
3209     },
3210
3211     // overridden Element method
3212     setVisible : function(v, a, d, c, e){
3213         if(v){
3214             this.showAction();
3215         }
3216         if(a && v){
3217             var cb = function(){
3218                 this.sync(true);
3219                 if(c){
3220                     c();
3221                 }
3222             }.createDelegate(this);
3223             supr.setVisible.call(this, true, true, d, cb, e);
3224         }else{
3225             if(!v){
3226                 this.hideUnders(true);
3227             }
3228             var cb = c;
3229             if(a){
3230                 cb = function(){
3231                     this.hideAction();
3232                     if(c){
3233                         c();
3234                     }
3235                 }.createDelegate(this);
3236             }
3237             supr.setVisible.call(this, v, a, d, cb, e);
3238             if(v){
3239                 this.sync(true);
3240             }else if(!a){
3241                 this.hideAction();
3242             }
3243         }
3244     },
3245
3246     storeXY : function(xy){
3247         delete this.lastLT;
3248         this.lastXY = xy;
3249     },
3250
3251     storeLeftTop : function(left, top){
3252         delete this.lastXY;
3253         this.lastLT = [left, top];
3254     },
3255
3256     // private
3257     beforeFx : function(){
3258         this.beforeAction();
3259         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
3260     },
3261
3262     // private
3263     afterFx : function(){
3264         Roo.Layer.superclass.afterFx.apply(this, arguments);
3265         this.sync(this.isVisible());
3266     },
3267
3268     // private
3269     beforeAction : function(){
3270         if(!this.updating && this.shadow){
3271             this.shadow.hide();
3272         }
3273     },
3274
3275     // overridden Element method
3276     setLeft : function(left){
3277         this.storeLeftTop(left, this.getTop(true));
3278         supr.setLeft.apply(this, arguments);
3279         this.sync();
3280     },
3281
3282     setTop : function(top){
3283         this.storeLeftTop(this.getLeft(true), top);
3284         supr.setTop.apply(this, arguments);
3285         this.sync();
3286     },
3287
3288     setLeftTop : function(left, top){
3289         this.storeLeftTop(left, top);
3290         supr.setLeftTop.apply(this, arguments);
3291         this.sync();
3292     },
3293
3294     setXY : function(xy, a, d, c, e){
3295         this.fixDisplay();
3296         this.beforeAction();
3297         this.storeXY(xy);
3298         var cb = this.createCB(c);
3299         supr.setXY.call(this, xy, a, d, cb, e);
3300         if(!a){
3301             cb();
3302         }
3303     },
3304
3305     // private
3306     createCB : function(c){
3307         var el = this;
3308         return function(){
3309             el.constrainXY();
3310             el.sync(true);
3311             if(c){
3312                 c();
3313             }
3314         };
3315     },
3316
3317     // overridden Element method
3318     setX : function(x, a, d, c, e){
3319         this.setXY([x, this.getY()], a, d, c, e);
3320     },
3321
3322     // overridden Element method
3323     setY : function(y, a, d, c, e){
3324         this.setXY([this.getX(), y], a, d, c, e);
3325     },
3326
3327     // overridden Element method
3328     setSize : function(w, h, a, d, c, e){
3329         this.beforeAction();
3330         var cb = this.createCB(c);
3331         supr.setSize.call(this, w, h, a, d, cb, e);
3332         if(!a){
3333             cb();
3334         }
3335     },
3336
3337     // overridden Element method
3338     setWidth : function(w, a, d, c, e){
3339         this.beforeAction();
3340         var cb = this.createCB(c);
3341         supr.setWidth.call(this, w, a, d, cb, e);
3342         if(!a){
3343             cb();
3344         }
3345     },
3346
3347     // overridden Element method
3348     setHeight : function(h, a, d, c, e){
3349         this.beforeAction();
3350         var cb = this.createCB(c);
3351         supr.setHeight.call(this, h, a, d, cb, e);
3352         if(!a){
3353             cb();
3354         }
3355     },
3356
3357     // overridden Element method
3358     setBounds : function(x, y, w, h, a, d, c, e){
3359         this.beforeAction();
3360         var cb = this.createCB(c);
3361         if(!a){
3362             this.storeXY([x, y]);
3363             supr.setXY.call(this, [x, y]);
3364             supr.setSize.call(this, w, h, a, d, cb, e);
3365             cb();
3366         }else{
3367             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
3368         }
3369         return this;
3370     },
3371     
3372     /**
3373      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
3374      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
3375      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
3376      * @param {Number} zindex The new z-index to set
3377      * @return {this} The Layer
3378      */
3379     setZIndex : function(zindex){
3380         this.zindex = zindex;
3381         this.setStyle("z-index", zindex + 2);
3382         if(this.shadow){
3383             this.shadow.setZIndex(zindex + 1);
3384         }
3385         if(this.shim){
3386             this.shim.setStyle("z-index", zindex);
3387         }
3388     }
3389 });
3390 })();/*
3391  * Based on:
3392  * Ext JS Library 1.1.1
3393  * Copyright(c) 2006-2007, Ext JS, LLC.
3394  *
3395  * Originally Released Under LGPL - original licence link has changed is not relivant.
3396  *
3397  * Fork - LGPL
3398  * <script type="text/javascript">
3399  */
3400
3401
3402 /**
3403  * @class Roo.Shadow
3404  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
3405  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
3406  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
3407  * @constructor
3408  * Create a new Shadow
3409  * @param {Object} config The config object
3410  */
3411 Roo.Shadow = function(config){
3412     Roo.apply(this, config);
3413     if(typeof this.mode != "string"){
3414         this.mode = this.defaultMode;
3415     }
3416     var o = this.offset, a = {h: 0};
3417     var rad = Math.floor(this.offset/2);
3418     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
3419         case "drop":
3420             a.w = 0;
3421             a.l = a.t = o;
3422             a.t -= 1;
3423             if(Roo.isIE){
3424                 a.l -= this.offset + rad;
3425                 a.t -= this.offset + rad;
3426                 a.w -= rad;
3427                 a.h -= rad;
3428                 a.t += 1;
3429             }
3430         break;
3431         case "sides":
3432             a.w = (o*2);
3433             a.l = -o;
3434             a.t = o-1;
3435             if(Roo.isIE){
3436                 a.l -= (this.offset - rad);
3437                 a.t -= this.offset + rad;
3438                 a.l += 1;
3439                 a.w -= (this.offset - rad)*2;
3440                 a.w -= rad + 1;
3441                 a.h -= 1;
3442             }
3443         break;
3444         case "frame":
3445             a.w = a.h = (o*2);
3446             a.l = a.t = -o;
3447             a.t += 1;
3448             a.h -= 2;
3449             if(Roo.isIE){
3450                 a.l -= (this.offset - rad);
3451                 a.t -= (this.offset - rad);
3452                 a.l += 1;
3453                 a.w -= (this.offset + rad + 1);
3454                 a.h -= (this.offset + rad);
3455                 a.h += 1;
3456             }
3457         break;
3458     };
3459
3460     this.adjusts = a;
3461 };
3462
3463 Roo.Shadow.prototype = {
3464     /**
3465      * @cfg {String} mode
3466      * The shadow display mode.  Supports the following options:<br />
3467      * sides: Shadow displays on both sides and bottom only<br />
3468      * frame: Shadow displays equally on all four sides<br />
3469      * drop: Traditional bottom-right drop shadow (default)
3470      */
3471     /**
3472      * @cfg {String} offset
3473      * The number of pixels to offset the shadow from the element (defaults to 4)
3474      */
3475     offset: 4,
3476
3477     // private
3478     defaultMode: "drop",
3479
3480     /**
3481      * Displays the shadow under the target element
3482      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
3483      */
3484     show : function(target){
3485         target = Roo.get(target);
3486         if(!this.el){
3487             this.el = Roo.Shadow.Pool.pull();
3488             if(this.el.dom.nextSibling != target.dom){
3489                 this.el.insertBefore(target);
3490             }
3491         }
3492         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
3493         if(Roo.isIE){
3494             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
3495         }
3496         this.realign(
3497             target.getLeft(true),
3498             target.getTop(true),
3499             target.getWidth(),
3500             target.getHeight()
3501         );
3502         this.el.dom.style.display = "block";
3503     },
3504
3505     /**
3506      * Returns true if the shadow is visible, else false
3507      */
3508     isVisible : function(){
3509         return this.el ? true : false;  
3510     },
3511
3512     /**
3513      * Direct alignment when values are already available. Show must be called at least once before
3514      * calling this method to ensure it is initialized.
3515      * @param {Number} left The target element left position
3516      * @param {Number} top The target element top position
3517      * @param {Number} width The target element width
3518      * @param {Number} height The target element height
3519      */
3520     realign : function(l, t, w, h){
3521         if(!this.el){
3522             return;
3523         }
3524         var a = this.adjusts, d = this.el.dom, s = d.style;
3525         var iea = 0;
3526         s.left = (l+a.l)+"px";
3527         s.top = (t+a.t)+"px";
3528         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
3529  
3530         if(s.width != sws || s.height != shs){
3531             s.width = sws;
3532             s.height = shs;
3533             if(!Roo.isIE){
3534                 var cn = d.childNodes;
3535                 var sww = Math.max(0, (sw-12))+"px";
3536                 cn[0].childNodes[1].style.width = sww;
3537                 cn[1].childNodes[1].style.width = sww;
3538                 cn[2].childNodes[1].style.width = sww;
3539                 cn[1].style.height = Math.max(0, (sh-12))+"px";
3540             }
3541         }
3542     },
3543
3544     /**
3545      * Hides this shadow
3546      */
3547     hide : function(){
3548         if(this.el){
3549             this.el.dom.style.display = "none";
3550             Roo.Shadow.Pool.push(this.el);
3551             delete this.el;
3552         }
3553     },
3554
3555     /**
3556      * Adjust the z-index of this shadow
3557      * @param {Number} zindex The new z-index
3558      */
3559     setZIndex : function(z){
3560         this.zIndex = z;
3561         if(this.el){
3562             this.el.setStyle("z-index", z);
3563         }
3564     }
3565 };
3566
3567 // Private utility class that manages the internal Shadow cache
3568 Roo.Shadow.Pool = function(){
3569     var p = [];
3570     var markup = Roo.isIE ?
3571                  '<div class="x-ie-shadow"></div>' :
3572                  '<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>';
3573     return {
3574         pull : function(){
3575             var sh = p.shift();
3576             if(!sh){
3577                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
3578                 sh.autoBoxAdjust = false;
3579             }
3580             return sh;
3581         },
3582
3583         push : function(sh){
3584             p.push(sh);
3585         }
3586     };
3587 }();/*
3588  * Based on:
3589  * Ext JS Library 1.1.1
3590  * Copyright(c) 2006-2007, Ext JS, LLC.
3591  *
3592  * Originally Released Under LGPL - original licence link has changed is not relivant.
3593  *
3594  * Fork - LGPL
3595  * <script type="text/javascript">
3596  */
3597
3598
3599 /**
3600  * @class Roo.SplitBar
3601  * @extends Roo.util.Observable
3602  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
3603  * <br><br>
3604  * Usage:
3605  * <pre><code>
3606 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
3607                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
3608 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
3609 split.minSize = 100;
3610 split.maxSize = 600;
3611 split.animate = true;
3612 split.on('moved', splitterMoved);
3613 </code></pre>
3614  * @constructor
3615  * Create a new SplitBar
3616  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
3617  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
3618  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3619  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
3620                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
3621                         position of the SplitBar).
3622  */
3623 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
3624     
3625     /** @private */
3626     this.el = Roo.get(dragElement, true);
3627     this.el.dom.unselectable = "on";
3628     /** @private */
3629     this.resizingEl = Roo.get(resizingElement, true);
3630
3631     /**
3632      * @private
3633      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3634      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
3635      * @type Number
3636      */
3637     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
3638     
3639     /**
3640      * The minimum size of the resizing element. (Defaults to 0)
3641      * @type Number
3642      */
3643     this.minSize = 0;
3644     
3645     /**
3646      * The maximum size of the resizing element. (Defaults to 2000)
3647      * @type Number
3648      */
3649     this.maxSize = 2000;
3650     
3651     /**
3652      * Whether to animate the transition to the new size
3653      * @type Boolean
3654      */
3655     this.animate = false;
3656     
3657     /**
3658      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
3659      * @type Boolean
3660      */
3661     this.useShim = false;
3662     
3663     /** @private */
3664     this.shim = null;
3665     
3666     if(!existingProxy){
3667         /** @private */
3668         this.proxy = Roo.SplitBar.createProxy(this.orientation);
3669     }else{
3670         this.proxy = Roo.get(existingProxy).dom;
3671     }
3672     /** @private */
3673     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
3674     
3675     /** @private */
3676     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
3677     
3678     /** @private */
3679     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
3680     
3681     /** @private */
3682     this.dragSpecs = {};
3683     
3684     /**
3685      * @private The adapter to use to positon and resize elements
3686      */
3687     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
3688     this.adapter.init(this);
3689     
3690     if(this.orientation == Roo.SplitBar.HORIZONTAL){
3691         /** @private */
3692         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
3693         this.el.addClass("x-splitbar-h");
3694     }else{
3695         /** @private */
3696         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
3697         this.el.addClass("x-splitbar-v");
3698     }
3699     
3700     this.addEvents({
3701         /**
3702          * @event resize
3703          * Fires when the splitter is moved (alias for {@link #event-moved})
3704          * @param {Roo.SplitBar} this
3705          * @param {Number} newSize the new width or height
3706          */
3707         "resize" : true,
3708         /**
3709          * @event moved
3710          * Fires when the splitter is moved
3711          * @param {Roo.SplitBar} this
3712          * @param {Number} newSize the new width or height
3713          */
3714         "moved" : true,
3715         /**
3716          * @event beforeresize
3717          * Fires before the splitter is dragged
3718          * @param {Roo.SplitBar} this
3719          */
3720         "beforeresize" : true,
3721
3722         "beforeapply" : true
3723     });
3724
3725     Roo.util.Observable.call(this);
3726 };
3727
3728 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
3729     onStartProxyDrag : function(x, y){
3730         this.fireEvent("beforeresize", this);
3731         if(!this.overlay){
3732             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
3733             o.unselectable();
3734             o.enableDisplayMode("block");
3735             // all splitbars share the same overlay
3736             Roo.SplitBar.prototype.overlay = o;
3737         }
3738         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
3739         this.overlay.show();
3740         Roo.get(this.proxy).setDisplayed("block");
3741         var size = this.adapter.getElementSize(this);
3742         this.activeMinSize = this.getMinimumSize();;
3743         this.activeMaxSize = this.getMaximumSize();;
3744         var c1 = size - this.activeMinSize;
3745         var c2 = Math.max(this.activeMaxSize - size, 0);
3746         if(this.orientation == Roo.SplitBar.HORIZONTAL){
3747             this.dd.resetConstraints();
3748             this.dd.setXConstraint(
3749                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
3750                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
3751             );
3752             this.dd.setYConstraint(0, 0);
3753         }else{
3754             this.dd.resetConstraints();
3755             this.dd.setXConstraint(0, 0);
3756             this.dd.setYConstraint(
3757                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
3758                 this.placement == Roo.SplitBar.TOP ? c2 : c1
3759             );
3760          }
3761         this.dragSpecs.startSize = size;
3762         this.dragSpecs.startPoint = [x, y];
3763         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
3764     },
3765     
3766     /** 
3767      * @private Called after the drag operation by the DDProxy
3768      */
3769     onEndProxyDrag : function(e){
3770         Roo.get(this.proxy).setDisplayed(false);
3771         var endPoint = Roo.lib.Event.getXY(e);
3772         if(this.overlay){
3773             this.overlay.hide();
3774         }
3775         var newSize;
3776         if(this.orientation == Roo.SplitBar.HORIZONTAL){
3777             newSize = this.dragSpecs.startSize + 
3778                 (this.placement == Roo.SplitBar.LEFT ?
3779                     endPoint[0] - this.dragSpecs.startPoint[0] :
3780                     this.dragSpecs.startPoint[0] - endPoint[0]
3781                 );
3782         }else{
3783             newSize = this.dragSpecs.startSize + 
3784                 (this.placement == Roo.SplitBar.TOP ?
3785                     endPoint[1] - this.dragSpecs.startPoint[1] :
3786                     this.dragSpecs.startPoint[1] - endPoint[1]
3787                 );
3788         }
3789         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
3790         if(newSize != this.dragSpecs.startSize){
3791             if(this.fireEvent('beforeapply', this, newSize) !== false){
3792                 this.adapter.setElementSize(this, newSize);
3793                 this.fireEvent("moved", this, newSize);
3794                 this.fireEvent("resize", this, newSize);
3795             }
3796         }
3797     },
3798     
3799     /**
3800      * Get the adapter this SplitBar uses
3801      * @return The adapter object
3802      */
3803     getAdapter : function(){
3804         return this.adapter;
3805     },
3806     
3807     /**
3808      * Set the adapter this SplitBar uses
3809      * @param {Object} adapter A SplitBar adapter object
3810      */
3811     setAdapter : function(adapter){
3812         this.adapter = adapter;
3813         this.adapter.init(this);
3814     },
3815     
3816     /**
3817      * Gets the minimum size for the resizing element
3818      * @return {Number} The minimum size
3819      */
3820     getMinimumSize : function(){
3821         return this.minSize;
3822     },
3823     
3824     /**
3825      * Sets the minimum size for the resizing element
3826      * @param {Number} minSize The minimum size
3827      */
3828     setMinimumSize : function(minSize){
3829         this.minSize = minSize;
3830     },
3831     
3832     /**
3833      * Gets the maximum size for the resizing element
3834      * @return {Number} The maximum size
3835      */
3836     getMaximumSize : function(){
3837         return this.maxSize;
3838     },
3839     
3840     /**
3841      * Sets the maximum size for the resizing element
3842      * @param {Number} maxSize The maximum size
3843      */
3844     setMaximumSize : function(maxSize){
3845         this.maxSize = maxSize;
3846     },
3847     
3848     /**
3849      * Sets the initialize size for the resizing element
3850      * @param {Number} size The initial size
3851      */
3852     setCurrentSize : function(size){
3853         var oldAnimate = this.animate;
3854         this.animate = false;
3855         this.adapter.setElementSize(this, size);
3856         this.animate = oldAnimate;
3857     },
3858     
3859     /**
3860      * Destroy this splitbar. 
3861      * @param {Boolean} removeEl True to remove the element
3862      */
3863     destroy : function(removeEl){
3864         if(this.shim){
3865             this.shim.remove();
3866         }
3867         this.dd.unreg();
3868         this.proxy.parentNode.removeChild(this.proxy);
3869         if(removeEl){
3870             this.el.remove();
3871         }
3872     }
3873 });
3874
3875 /**
3876  * @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.
3877  */
3878 Roo.SplitBar.createProxy = function(dir){
3879     var proxy = new Roo.Element(document.createElement("div"));
3880     proxy.unselectable();
3881     var cls = 'x-splitbar-proxy';
3882     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
3883     document.body.appendChild(proxy.dom);
3884     return proxy.dom;
3885 };
3886
3887 /** 
3888  * @class Roo.SplitBar.BasicLayoutAdapter
3889  * Default Adapter. It assumes the splitter and resizing element are not positioned
3890  * elements and only gets/sets the width of the element. Generally used for table based layouts.
3891  */
3892 Roo.SplitBar.BasicLayoutAdapter = function(){
3893 };
3894
3895 Roo.SplitBar.BasicLayoutAdapter.prototype = {
3896     // do nothing for now
3897     init : function(s){
3898     
3899     },
3900     /**
3901      * Called before drag operations to get the current size of the resizing element. 
3902      * @param {Roo.SplitBar} s The SplitBar using this adapter
3903      */
3904      getElementSize : function(s){
3905         if(s.orientation == Roo.SplitBar.HORIZONTAL){
3906             return s.resizingEl.getWidth();
3907         }else{
3908             return s.resizingEl.getHeight();
3909         }
3910     },
3911     
3912     /**
3913      * Called after drag operations to set the size of the resizing element.
3914      * @param {Roo.SplitBar} s The SplitBar using this adapter
3915      * @param {Number} newSize The new size to set
3916      * @param {Function} onComplete A function to be invoked when resizing is complete
3917      */
3918     setElementSize : function(s, newSize, onComplete){
3919         if(s.orientation == Roo.SplitBar.HORIZONTAL){
3920             if(!s.animate){
3921                 s.resizingEl.setWidth(newSize);
3922                 if(onComplete){
3923                     onComplete(s, newSize);
3924                 }
3925             }else{
3926                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
3927             }
3928         }else{
3929             
3930             if(!s.animate){
3931                 s.resizingEl.setHeight(newSize);
3932                 if(onComplete){
3933                     onComplete(s, newSize);
3934                 }
3935             }else{
3936                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
3937             }
3938         }
3939     }
3940 };
3941
3942 /** 
3943  *@class Roo.SplitBar.AbsoluteLayoutAdapter
3944  * @extends Roo.SplitBar.BasicLayoutAdapter
3945  * Adapter that  moves the splitter element to align with the resized sizing element. 
3946  * Used with an absolute positioned SplitBar.
3947  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
3948  * document.body, make sure you assign an id to the body element.
3949  */
3950 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
3951     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
3952     this.container = Roo.get(container);
3953 };
3954
3955 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
3956     init : function(s){
3957         this.basic.init(s);
3958     },
3959     
3960     getElementSize : function(s){
3961         return this.basic.getElementSize(s);
3962     },
3963     
3964     setElementSize : function(s, newSize, onComplete){
3965         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
3966     },
3967     
3968     moveSplitter : function(s){
3969         var yes = Roo.SplitBar;
3970         switch(s.placement){
3971             case yes.LEFT:
3972                 s.el.setX(s.resizingEl.getRight());
3973                 break;
3974             case yes.RIGHT:
3975                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
3976                 break;
3977             case yes.TOP:
3978                 s.el.setY(s.resizingEl.getBottom());
3979                 break;
3980             case yes.BOTTOM:
3981                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
3982                 break;
3983         }
3984     }
3985 };
3986
3987 /**
3988  * Orientation constant - Create a vertical SplitBar
3989  * @static
3990  * @type Number
3991  */
3992 Roo.SplitBar.VERTICAL = 1;
3993
3994 /**
3995  * Orientation constant - Create a horizontal SplitBar
3996  * @static
3997  * @type Number
3998  */
3999 Roo.SplitBar.HORIZONTAL = 2;
4000
4001 /**
4002  * Placement constant - The resizing element is to the left of the splitter element
4003  * @static
4004  * @type Number
4005  */
4006 Roo.SplitBar.LEFT = 1;
4007
4008 /**
4009  * Placement constant - The resizing element is to the right of the splitter element
4010  * @static
4011  * @type Number
4012  */
4013 Roo.SplitBar.RIGHT = 2;
4014
4015 /**
4016  * Placement constant - The resizing element is positioned above the splitter element
4017  * @static
4018  * @type Number
4019  */
4020 Roo.SplitBar.TOP = 3;
4021
4022 /**
4023  * Placement constant - The resizing element is positioned under splitter element
4024  * @static
4025  * @type Number
4026  */
4027 Roo.SplitBar.BOTTOM = 4;
4028 /*
4029  * Based on:
4030  * Ext JS Library 1.1.1
4031  * Copyright(c) 2006-2007, Ext JS, LLC.
4032  *
4033  * Originally Released Under LGPL - original licence link has changed is not relivant.
4034  *
4035  * Fork - LGPL
4036  * <script type="text/javascript">
4037  */
4038
4039 /**
4040  * @class Roo.View
4041  * @extends Roo.util.Observable
4042  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
4043  * This class also supports single and multi selection modes. <br>
4044  * Create a data model bound view:
4045  <pre><code>
4046  var store = new Roo.data.Store(...);
4047
4048  var view = new Roo.View({
4049     el : "my-element",
4050     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
4051  
4052     singleSelect: true,
4053     selectedClass: "ydataview-selected",
4054     store: store
4055  });
4056
4057  // listen for node click?
4058  view.on("click", function(vw, index, node, e){
4059  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
4060  });
4061
4062  // load XML data
4063  dataModel.load("foobar.xml");
4064  </code></pre>
4065  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
4066  * <br><br>
4067  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
4068  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
4069  * 
4070  * Note: old style constructor is still suported (container, template, config)
4071  * 
4072  * @constructor
4073  * Create a new View
4074  * @param {Object} config The config object
4075  * 
4076  */
4077 Roo.View = function(config, depreciated_tpl, depreciated_config){
4078     
4079     this.parent = false;
4080     
4081     if (typeof(depreciated_tpl) == 'undefined') {
4082         // new way.. - universal constructor.
4083         Roo.apply(this, config);
4084         this.el  = Roo.get(this.el);
4085     } else {
4086         // old format..
4087         this.el  = Roo.get(config);
4088         this.tpl = depreciated_tpl;
4089         Roo.apply(this, depreciated_config);
4090     }
4091     this.wrapEl  = this.el.wrap().wrap();
4092     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
4093     
4094     
4095     if(typeof(this.tpl) == "string"){
4096         this.tpl = new Roo.Template(this.tpl);
4097     } else {
4098         // support xtype ctors..
4099         this.tpl = new Roo.factory(this.tpl, Roo);
4100     }
4101     
4102     
4103     this.tpl.compile();
4104     
4105     /** @private */
4106     this.addEvents({
4107         /**
4108          * @event beforeclick
4109          * Fires before a click is processed. Returns false to cancel the default action.
4110          * @param {Roo.View} this
4111          * @param {Number} index The index of the target node
4112          * @param {HTMLElement} node The target node
4113          * @param {Roo.EventObject} e The raw event object
4114          */
4115             "beforeclick" : true,
4116         /**
4117          * @event click
4118          * Fires when a template node is clicked.
4119          * @param {Roo.View} this
4120          * @param {Number} index The index of the target node
4121          * @param {HTMLElement} node The target node
4122          * @param {Roo.EventObject} e The raw event object
4123          */
4124             "click" : true,
4125         /**
4126          * @event dblclick
4127          * Fires when a template node is double clicked.
4128          * @param {Roo.View} this
4129          * @param {Number} index The index of the target node
4130          * @param {HTMLElement} node The target node
4131          * @param {Roo.EventObject} e The raw event object
4132          */
4133             "dblclick" : true,
4134         /**
4135          * @event contextmenu
4136          * Fires when a template node is right clicked.
4137          * @param {Roo.View} this
4138          * @param {Number} index The index of the target node
4139          * @param {HTMLElement} node The target node
4140          * @param {Roo.EventObject} e The raw event object
4141          */
4142             "contextmenu" : true,
4143         /**
4144          * @event selectionchange
4145          * Fires when the selected nodes change.
4146          * @param {Roo.View} this
4147          * @param {Array} selections Array of the selected nodes
4148          */
4149             "selectionchange" : true,
4150     
4151         /**
4152          * @event beforeselect
4153          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
4154          * @param {Roo.View} this
4155          * @param {HTMLElement} node The node to be selected
4156          * @param {Array} selections Array of currently selected nodes
4157          */
4158             "beforeselect" : true,
4159         /**
4160          * @event preparedata
4161          * Fires on every row to render, to allow you to change the data.
4162          * @param {Roo.View} this
4163          * @param {Object} data to be rendered (change this)
4164          */
4165           "preparedata" : true
4166           
4167           
4168         });
4169
4170
4171
4172     this.el.on({
4173         "click": this.onClick,
4174         "dblclick": this.onDblClick,
4175         "contextmenu": this.onContextMenu,
4176         scope:this
4177     });
4178
4179     this.selections = [];
4180     this.nodes = [];
4181     this.cmp = new Roo.CompositeElementLite([]);
4182     if(this.store){
4183         this.store = Roo.factory(this.store, Roo.data);
4184         this.setStore(this.store, true);
4185     }
4186     
4187     if ( this.footer && this.footer.xtype) {
4188            
4189          var fctr = this.wrapEl.appendChild(document.createElement("div"));
4190         
4191         this.footer.dataSource = this.store;
4192         this.footer.container = fctr;
4193         this.footer = Roo.factory(this.footer, Roo);
4194         fctr.insertFirst(this.el);
4195         
4196         // this is a bit insane - as the paging toolbar seems to detach the el..
4197 //        dom.parentNode.parentNode.parentNode
4198          // they get detached?
4199     }
4200     
4201     
4202     Roo.View.superclass.constructor.call(this);
4203     
4204     
4205 };
4206
4207 Roo.extend(Roo.View, Roo.util.Observable, {
4208     
4209      /**
4210      * @cfg {Roo.data.Store} store Data store to load data from.
4211      */
4212     store : false,
4213     
4214     /**
4215      * @cfg {String|Roo.Element} el The container element.
4216      */
4217     el : '',
4218     
4219     /**
4220      * @cfg {String|Roo.Template} tpl The template used by this View 
4221      */
4222     tpl : false,
4223     /**
4224      * @cfg {String} dataName the named area of the template to use as the data area
4225      *                          Works with domtemplates roo-name="name"
4226      */
4227     dataName: false,
4228     /**
4229      * @cfg {String} selectedClass The css class to add to selected nodes
4230      */
4231     selectedClass : "x-view-selected",
4232      /**
4233      * @cfg {String} emptyText The empty text to show when nothing is loaded.
4234      */
4235     emptyText : "",
4236     
4237     /**
4238      * @cfg {String} text to display on mask (default Loading)
4239      */
4240     mask : false,
4241     /**
4242      * @cfg {Boolean} multiSelect Allow multiple selection
4243      */
4244     multiSelect : false,
4245     /**
4246      * @cfg {Boolean} singleSelect Allow single selection
4247      */
4248     singleSelect:  false,
4249     
4250     /**
4251      * @cfg {Boolean} toggleSelect - selecting 
4252      */
4253     toggleSelect : false,
4254     
4255     /**
4256      * @cfg {Boolean} tickable - selecting 
4257      */
4258     tickable : false,
4259     
4260     /**
4261      * Returns the element this view is bound to.
4262      * @return {Roo.Element}
4263      */
4264     getEl : function(){
4265         return this.wrapEl;
4266     },
4267     
4268     
4269
4270     /**
4271      * Refreshes the view. - called by datachanged on the store. - do not call directly.
4272      */
4273     refresh : function(){
4274         //Roo.log('refresh');
4275         var t = this.tpl;
4276         
4277         // if we are using something like 'domtemplate', then
4278         // the what gets used is:
4279         // t.applySubtemplate(NAME, data, wrapping data..)
4280         // the outer template then get' applied with
4281         //     the store 'extra data'
4282         // and the body get's added to the
4283         //      roo-name="data" node?
4284         //      <span class='roo-tpl-{name}'></span> ?????
4285         
4286         
4287         
4288         this.clearSelections();
4289         this.el.update("");
4290         var html = [];
4291         var records = this.store.getRange();
4292         if(records.length < 1) {
4293             
4294             // is this valid??  = should it render a template??
4295             
4296             this.el.update(this.emptyText);
4297             return;
4298         }
4299         var el = this.el;
4300         if (this.dataName) {
4301             this.el.update(t.apply(this.store.meta)); //????
4302             el = this.el.child('.roo-tpl-' + this.dataName);
4303         }
4304         
4305         for(var i = 0, len = records.length; i < len; i++){
4306             var data = this.prepareData(records[i].data, i, records[i]);
4307             this.fireEvent("preparedata", this, data, i, records[i]);
4308             
4309             var d = Roo.apply({}, data);
4310             
4311             if(this.tickable){
4312                 Roo.apply(d, {'roo-id' : Roo.id()});
4313                 
4314                 var _this = this;
4315             
4316                 Roo.each(this.parent.item, function(item){
4317                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
4318                         return;
4319                     }
4320                     Roo.apply(d, {'roo-data-checked' : 'checked'});
4321                 });
4322             }
4323             
4324             html[html.length] = Roo.util.Format.trim(
4325                 this.dataName ?
4326                     t.applySubtemplate(this.dataName, d, this.store.meta) :
4327                     t.apply(d)
4328             );
4329         }
4330         
4331         
4332         
4333         el.update(html.join(""));
4334         this.nodes = el.dom.childNodes;
4335         this.updateIndexes(0);
4336     },
4337     
4338
4339     /**
4340      * Function to override to reformat the data that is sent to
4341      * the template for each node.
4342      * DEPRICATED - use the preparedata event handler.
4343      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
4344      * a JSON object for an UpdateManager bound view).
4345      */
4346     prepareData : function(data, index, record)
4347     {
4348         this.fireEvent("preparedata", this, data, index, record);
4349         return data;
4350     },
4351
4352     onUpdate : function(ds, record){
4353         // Roo.log('on update');   
4354         this.clearSelections();
4355         var index = this.store.indexOf(record);
4356         var n = this.nodes[index];
4357         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
4358         n.parentNode.removeChild(n);
4359         this.updateIndexes(index, index);
4360     },
4361
4362     
4363     
4364 // --------- FIXME     
4365     onAdd : function(ds, records, index)
4366     {
4367         //Roo.log(['on Add', ds, records, index] );        
4368         this.clearSelections();
4369         if(this.nodes.length == 0){
4370             this.refresh();
4371             return;
4372         }
4373         var n = this.nodes[index];
4374         for(var i = 0, len = records.length; i < len; i++){
4375             var d = this.prepareData(records[i].data, i, records[i]);
4376             if(n){
4377                 this.tpl.insertBefore(n, d);
4378             }else{
4379                 
4380                 this.tpl.append(this.el, d);
4381             }
4382         }
4383         this.updateIndexes(index);
4384     },
4385
4386     onRemove : function(ds, record, index){
4387        // Roo.log('onRemove');
4388         this.clearSelections();
4389         var el = this.dataName  ?
4390             this.el.child('.roo-tpl-' + this.dataName) :
4391             this.el; 
4392         
4393         el.dom.removeChild(this.nodes[index]);
4394         this.updateIndexes(index);
4395     },
4396
4397     /**
4398      * Refresh an individual node.
4399      * @param {Number} index
4400      */
4401     refreshNode : function(index){
4402         this.onUpdate(this.store, this.store.getAt(index));
4403     },
4404
4405     updateIndexes : function(startIndex, endIndex){
4406         var ns = this.nodes;
4407         startIndex = startIndex || 0;
4408         endIndex = endIndex || ns.length - 1;
4409         for(var i = startIndex; i <= endIndex; i++){
4410             ns[i].nodeIndex = i;
4411         }
4412     },
4413
4414     /**
4415      * Changes the data store this view uses and refresh the view.
4416      * @param {Store} store
4417      */
4418     setStore : function(store, initial){
4419         if(!initial && this.store){
4420             this.store.un("datachanged", this.refresh);
4421             this.store.un("add", this.onAdd);
4422             this.store.un("remove", this.onRemove);
4423             this.store.un("update", this.onUpdate);
4424             this.store.un("clear", this.refresh);
4425             this.store.un("beforeload", this.onBeforeLoad);
4426             this.store.un("load", this.onLoad);
4427             this.store.un("loadexception", this.onLoad);
4428         }
4429         if(store){
4430           
4431             store.on("datachanged", this.refresh, this);
4432             store.on("add", this.onAdd, this);
4433             store.on("remove", this.onRemove, this);
4434             store.on("update", this.onUpdate, this);
4435             store.on("clear", this.refresh, this);
4436             store.on("beforeload", this.onBeforeLoad, this);
4437             store.on("load", this.onLoad, this);
4438             store.on("loadexception", this.onLoad, this);
4439         }
4440         
4441         if(store){
4442             this.refresh();
4443         }
4444     },
4445     /**
4446      * onbeforeLoad - masks the loading area.
4447      *
4448      */
4449     onBeforeLoad : function(store,opts)
4450     {
4451          //Roo.log('onBeforeLoad');   
4452         if (!opts.add) {
4453             this.el.update("");
4454         }
4455         this.el.mask(this.mask ? this.mask : "Loading" ); 
4456     },
4457     onLoad : function ()
4458     {
4459         this.el.unmask();
4460     },
4461     
4462
4463     /**
4464      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
4465      * @param {HTMLElement} node
4466      * @return {HTMLElement} The template node
4467      */
4468     findItemFromChild : function(node){
4469         var el = this.dataName  ?
4470             this.el.child('.roo-tpl-' + this.dataName,true) :
4471             this.el.dom; 
4472         
4473         if(!node || node.parentNode == el){
4474                     return node;
4475             }
4476             var p = node.parentNode;
4477             while(p && p != el){
4478             if(p.parentNode == el){
4479                 return p;
4480             }
4481             p = p.parentNode;
4482         }
4483             return null;
4484     },
4485
4486     /** @ignore */
4487     onClick : function(e){
4488         var item = this.findItemFromChild(e.getTarget());
4489         if(item){
4490             var index = this.indexOf(item);
4491             if(this.onItemClick(item, index, e) !== false){
4492                 this.fireEvent("click", this, index, item, e);
4493             }
4494         }else{
4495             this.clearSelections();
4496         }
4497     },
4498
4499     /** @ignore */
4500     onContextMenu : function(e){
4501         var item = this.findItemFromChild(e.getTarget());
4502         if(item){
4503             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
4504         }
4505     },
4506
4507     /** @ignore */
4508     onDblClick : function(e){
4509         var item = this.findItemFromChild(e.getTarget());
4510         if(item){
4511             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
4512         }
4513     },
4514
4515     onItemClick : function(item, index, e)
4516     {
4517         if(this.fireEvent("beforeclick", this, index, item, e) === false){
4518             return false;
4519         }
4520         if (this.toggleSelect) {
4521             var m = this.isSelected(item) ? 'unselect' : 'select';
4522             //Roo.log(m);
4523             var _t = this;
4524             _t[m](item, true, false);
4525             return true;
4526         }
4527         if(this.multiSelect || this.singleSelect){
4528             if(this.multiSelect && e.shiftKey && this.lastSelection){
4529                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
4530             }else{
4531                 this.select(item, this.multiSelect && e.ctrlKey);
4532                 this.lastSelection = item;
4533             }
4534             
4535             if(!this.tickable){
4536                 e.preventDefault();
4537             }
4538             
4539         }
4540         return true;
4541     },
4542
4543     /**
4544      * Get the number of selected nodes.
4545      * @return {Number}
4546      */
4547     getSelectionCount : function(){
4548         return this.selections.length;
4549     },
4550
4551     /**
4552      * Get the currently selected nodes.
4553      * @return {Array} An array of HTMLElements
4554      */
4555     getSelectedNodes : function(){
4556         return this.selections;
4557     },
4558
4559     /**
4560      * Get the indexes of the selected nodes.
4561      * @return {Array}
4562      */
4563     getSelectedIndexes : function(){
4564         var indexes = [], s = this.selections;
4565         for(var i = 0, len = s.length; i < len; i++){
4566             indexes.push(s[i].nodeIndex);
4567         }
4568         return indexes;
4569     },
4570
4571     /**
4572      * Clear all selections
4573      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
4574      */
4575     clearSelections : function(suppressEvent){
4576         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
4577             this.cmp.elements = this.selections;
4578             this.cmp.removeClass(this.selectedClass);
4579             this.selections = [];
4580             if(!suppressEvent){
4581                 this.fireEvent("selectionchange", this, this.selections);
4582             }
4583         }
4584     },
4585
4586     /**
4587      * Returns true if the passed node is selected
4588      * @param {HTMLElement/Number} node The node or node index
4589      * @return {Boolean}
4590      */
4591     isSelected : function(node){
4592         var s = this.selections;
4593         if(s.length < 1){
4594             return false;
4595         }
4596         node = this.getNode(node);
4597         return s.indexOf(node) !== -1;
4598     },
4599
4600     /**
4601      * Selects nodes.
4602      * @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
4603      * @param {Boolean} keepExisting (optional) true to keep existing selections
4604      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4605      */
4606     select : function(nodeInfo, keepExisting, suppressEvent){
4607         if(nodeInfo instanceof Array){
4608             if(!keepExisting){
4609                 this.clearSelections(true);
4610             }
4611             for(var i = 0, len = nodeInfo.length; i < len; i++){
4612                 this.select(nodeInfo[i], true, true);
4613             }
4614             return;
4615         } 
4616         var node = this.getNode(nodeInfo);
4617         if(!node || this.isSelected(node)){
4618             return; // already selected.
4619         }
4620         if(!keepExisting){
4621             this.clearSelections(true);
4622         }
4623         
4624         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
4625             Roo.fly(node).addClass(this.selectedClass);
4626             this.selections.push(node);
4627             if(!suppressEvent){
4628                 this.fireEvent("selectionchange", this, this.selections);
4629             }
4630         }
4631         
4632         
4633     },
4634       /**
4635      * Unselects nodes.
4636      * @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
4637      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
4638      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4639      */
4640     unselect : function(nodeInfo, keepExisting, suppressEvent)
4641     {
4642         if(nodeInfo instanceof Array){
4643             Roo.each(this.selections, function(s) {
4644                 this.unselect(s, nodeInfo);
4645             }, this);
4646             return;
4647         }
4648         var node = this.getNode(nodeInfo);
4649         if(!node || !this.isSelected(node)){
4650             //Roo.log("not selected");
4651             return; // not selected.
4652         }
4653         // fireevent???
4654         var ns = [];
4655         Roo.each(this.selections, function(s) {
4656             if (s == node ) {
4657                 Roo.fly(node).removeClass(this.selectedClass);
4658
4659                 return;
4660             }
4661             ns.push(s);
4662         },this);
4663         
4664         this.selections= ns;
4665         this.fireEvent("selectionchange", this, this.selections);
4666     },
4667
4668     /**
4669      * Gets a template node.
4670      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4671      * @return {HTMLElement} The node or null if it wasn't found
4672      */
4673     getNode : function(nodeInfo){
4674         if(typeof nodeInfo == "string"){
4675             return document.getElementById(nodeInfo);
4676         }else if(typeof nodeInfo == "number"){
4677             return this.nodes[nodeInfo];
4678         }
4679         return nodeInfo;
4680     },
4681
4682     /**
4683      * Gets a range template nodes.
4684      * @param {Number} startIndex
4685      * @param {Number} endIndex
4686      * @return {Array} An array of nodes
4687      */
4688     getNodes : function(start, end){
4689         var ns = this.nodes;
4690         start = start || 0;
4691         end = typeof end == "undefined" ? ns.length - 1 : end;
4692         var nodes = [];
4693         if(start <= end){
4694             for(var i = start; i <= end; i++){
4695                 nodes.push(ns[i]);
4696             }
4697         } else{
4698             for(var i = start; i >= end; i--){
4699                 nodes.push(ns[i]);
4700             }
4701         }
4702         return nodes;
4703     },
4704
4705     /**
4706      * Finds the index of the passed node
4707      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4708      * @return {Number} The index of the node or -1
4709      */
4710     indexOf : function(node){
4711         node = this.getNode(node);
4712         if(typeof node.nodeIndex == "number"){
4713             return node.nodeIndex;
4714         }
4715         var ns = this.nodes;
4716         for(var i = 0, len = ns.length; i < len; i++){
4717             if(ns[i] == node){
4718                 return i;
4719             }
4720         }
4721         return -1;
4722     }
4723 });
4724 /*
4725  * Based on:
4726  * Ext JS Library 1.1.1
4727  * Copyright(c) 2006-2007, Ext JS, LLC.
4728  *
4729  * Originally Released Under LGPL - original licence link has changed is not relivant.
4730  *
4731  * Fork - LGPL
4732  * <script type="text/javascript">
4733  */
4734
4735 /**
4736  * @class Roo.JsonView
4737  * @extends Roo.View
4738  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
4739 <pre><code>
4740 var view = new Roo.JsonView({
4741     container: "my-element",
4742     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
4743     multiSelect: true, 
4744     jsonRoot: "data" 
4745 });
4746
4747 // listen for node click?
4748 view.on("click", function(vw, index, node, e){
4749     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
4750 });
4751
4752 // direct load of JSON data
4753 view.load("foobar.php");
4754
4755 // Example from my blog list
4756 var tpl = new Roo.Template(
4757     '&lt;div class="entry"&gt;' +
4758     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
4759     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
4760     "&lt;/div&gt;&lt;hr /&gt;"
4761 );
4762
4763 var moreView = new Roo.JsonView({
4764     container :  "entry-list", 
4765     template : tpl,
4766     jsonRoot: "posts"
4767 });
4768 moreView.on("beforerender", this.sortEntries, this);
4769 moreView.load({
4770     url: "/blog/get-posts.php",
4771     params: "allposts=true",
4772     text: "Loading Blog Entries..."
4773 });
4774 </code></pre>
4775
4776 * Note: old code is supported with arguments : (container, template, config)
4777
4778
4779  * @constructor
4780  * Create a new JsonView
4781  * 
4782  * @param {Object} config The config object
4783  * 
4784  */
4785 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
4786     
4787     
4788     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
4789
4790     var um = this.el.getUpdateManager();
4791     um.setRenderer(this);
4792     um.on("update", this.onLoad, this);
4793     um.on("failure", this.onLoadException, this);
4794
4795     /**
4796      * @event beforerender
4797      * Fires before rendering of the downloaded JSON data.
4798      * @param {Roo.JsonView} this
4799      * @param {Object} data The JSON data loaded
4800      */
4801     /**
4802      * @event load
4803      * Fires when data is loaded.
4804      * @param {Roo.JsonView} this
4805      * @param {Object} data The JSON data loaded
4806      * @param {Object} response The raw Connect response object
4807      */
4808     /**
4809      * @event loadexception
4810      * Fires when loading fails.
4811      * @param {Roo.JsonView} this
4812      * @param {Object} response The raw Connect response object
4813      */
4814     this.addEvents({
4815         'beforerender' : true,
4816         'load' : true,
4817         'loadexception' : true
4818     });
4819 };
4820 Roo.extend(Roo.JsonView, Roo.View, {
4821     /**
4822      * @type {String} The root property in the loaded JSON object that contains the data
4823      */
4824     jsonRoot : "",
4825
4826     /**
4827      * Refreshes the view.
4828      */
4829     refresh : function(){
4830         this.clearSelections();
4831         this.el.update("");
4832         var html = [];
4833         var o = this.jsonData;
4834         if(o && o.length > 0){
4835             for(var i = 0, len = o.length; i < len; i++){
4836                 var data = this.prepareData(o[i], i, o);
4837                 html[html.length] = this.tpl.apply(data);
4838             }
4839         }else{
4840             html.push(this.emptyText);
4841         }
4842         this.el.update(html.join(""));
4843         this.nodes = this.el.dom.childNodes;
4844         this.updateIndexes(0);
4845     },
4846
4847     /**
4848      * 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.
4849      * @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:
4850      <pre><code>
4851      view.load({
4852          url: "your-url.php",
4853          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
4854          callback: yourFunction,
4855          scope: yourObject, //(optional scope)
4856          discardUrl: false,
4857          nocache: false,
4858          text: "Loading...",
4859          timeout: 30,
4860          scripts: false
4861      });
4862      </code></pre>
4863      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
4864      * 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.
4865      * @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}
4866      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
4867      * @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.
4868      */
4869     load : function(){
4870         var um = this.el.getUpdateManager();
4871         um.update.apply(um, arguments);
4872     },
4873
4874     // note - render is a standard framework call...
4875     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
4876     render : function(el, response){
4877         
4878         this.clearSelections();
4879         this.el.update("");
4880         var o;
4881         try{
4882             if (response != '') {
4883                 o = Roo.util.JSON.decode(response.responseText);
4884                 if(this.jsonRoot){
4885                     
4886                     o = o[this.jsonRoot];
4887                 }
4888             }
4889         } catch(e){
4890         }
4891         /**
4892          * The current JSON data or null
4893          */
4894         this.jsonData = o;
4895         this.beforeRender();
4896         this.refresh();
4897     },
4898
4899 /**
4900  * Get the number of records in the current JSON dataset
4901  * @return {Number}
4902  */
4903     getCount : function(){
4904         return this.jsonData ? this.jsonData.length : 0;
4905     },
4906
4907 /**
4908  * Returns the JSON object for the specified node(s)
4909  * @param {HTMLElement/Array} node The node or an array of nodes
4910  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
4911  * you get the JSON object for the node
4912  */
4913     getNodeData : function(node){
4914         if(node instanceof Array){
4915             var data = [];
4916             for(var i = 0, len = node.length; i < len; i++){
4917                 data.push(this.getNodeData(node[i]));
4918             }
4919             return data;
4920         }
4921         return this.jsonData[this.indexOf(node)] || null;
4922     },
4923
4924     beforeRender : function(){
4925         this.snapshot = this.jsonData;
4926         if(this.sortInfo){
4927             this.sort.apply(this, this.sortInfo);
4928         }
4929         this.fireEvent("beforerender", this, this.jsonData);
4930     },
4931
4932     onLoad : function(el, o){
4933         this.fireEvent("load", this, this.jsonData, o);
4934     },
4935
4936     onLoadException : function(el, o){
4937         this.fireEvent("loadexception", this, o);
4938     },
4939
4940 /**
4941  * Filter the data by a specific property.
4942  * @param {String} property A property on your JSON objects
4943  * @param {String/RegExp} value Either string that the property values
4944  * should start with, or a RegExp to test against the property
4945  */
4946     filter : function(property, value){
4947         if(this.jsonData){
4948             var data = [];
4949             var ss = this.snapshot;
4950             if(typeof value == "string"){
4951                 var vlen = value.length;
4952                 if(vlen == 0){
4953                     this.clearFilter();
4954                     return;
4955                 }
4956                 value = value.toLowerCase();
4957                 for(var i = 0, len = ss.length; i < len; i++){
4958                     var o = ss[i];
4959                     if(o[property].substr(0, vlen).toLowerCase() == value){
4960                         data.push(o);
4961                     }
4962                 }
4963             } else if(value.exec){ // regex?
4964                 for(var i = 0, len = ss.length; i < len; i++){
4965                     var o = ss[i];
4966                     if(value.test(o[property])){
4967                         data.push(o);
4968                     }
4969                 }
4970             } else{
4971                 return;
4972             }
4973             this.jsonData = data;
4974             this.refresh();
4975         }
4976     },
4977
4978 /**
4979  * Filter by a function. The passed function will be called with each
4980  * object in the current dataset. If the function returns true the value is kept,
4981  * otherwise it is filtered.
4982  * @param {Function} fn
4983  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
4984  */
4985     filterBy : function(fn, scope){
4986         if(this.jsonData){
4987             var data = [];
4988             var ss = this.snapshot;
4989             for(var i = 0, len = ss.length; i < len; i++){
4990                 var o = ss[i];
4991                 if(fn.call(scope || this, o)){
4992                     data.push(o);
4993                 }
4994             }
4995             this.jsonData = data;
4996             this.refresh();
4997         }
4998     },
4999
5000 /**
5001  * Clears the current filter.
5002  */
5003     clearFilter : function(){
5004         if(this.snapshot && this.jsonData != this.snapshot){
5005             this.jsonData = this.snapshot;
5006             this.refresh();
5007         }
5008     },
5009
5010
5011 /**
5012  * Sorts the data for this view and refreshes it.
5013  * @param {String} property A property on your JSON objects to sort on
5014  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
5015  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
5016  */
5017     sort : function(property, dir, sortType){
5018         this.sortInfo = Array.prototype.slice.call(arguments, 0);
5019         if(this.jsonData){
5020             var p = property;
5021             var dsc = dir && dir.toLowerCase() == "desc";
5022             var f = function(o1, o2){
5023                 var v1 = sortType ? sortType(o1[p]) : o1[p];
5024                 var v2 = sortType ? sortType(o2[p]) : o2[p];
5025                 ;
5026                 if(v1 < v2){
5027                     return dsc ? +1 : -1;
5028                 } else if(v1 > v2){
5029                     return dsc ? -1 : +1;
5030                 } else{
5031                     return 0;
5032                 }
5033             };
5034             this.jsonData.sort(f);
5035             this.refresh();
5036             if(this.jsonData != this.snapshot){
5037                 this.snapshot.sort(f);
5038             }
5039         }
5040     }
5041 });/*
5042  * Based on:
5043  * Ext JS Library 1.1.1
5044  * Copyright(c) 2006-2007, Ext JS, LLC.
5045  *
5046  * Originally Released Under LGPL - original licence link has changed is not relivant.
5047  *
5048  * Fork - LGPL
5049  * <script type="text/javascript">
5050  */
5051  
5052
5053 /**
5054  * @class Roo.ColorPalette
5055  * @extends Roo.Component
5056  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
5057  * Here's an example of typical usage:
5058  * <pre><code>
5059 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
5060 cp.render('my-div');
5061
5062 cp.on('select', function(palette, selColor){
5063     // do something with selColor
5064 });
5065 </code></pre>
5066  * @constructor
5067  * Create a new ColorPalette
5068  * @param {Object} config The config object
5069  */
5070 Roo.ColorPalette = function(config){
5071     Roo.ColorPalette.superclass.constructor.call(this, config);
5072     this.addEvents({
5073         /**
5074              * @event select
5075              * Fires when a color is selected
5076              * @param {ColorPalette} this
5077              * @param {String} color The 6-digit color hex code (without the # symbol)
5078              */
5079         select: true
5080     });
5081
5082     if(this.handler){
5083         this.on("select", this.handler, this.scope, true);
5084     }
5085 };
5086 Roo.extend(Roo.ColorPalette, Roo.Component, {
5087     /**
5088      * @cfg {String} itemCls
5089      * The CSS class to apply to the containing element (defaults to "x-color-palette")
5090      */
5091     itemCls : "x-color-palette",
5092     /**
5093      * @cfg {String} value
5094      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
5095      * the hex codes are case-sensitive.
5096      */
5097     value : null,
5098     clickEvent:'click',
5099     // private
5100     ctype: "Roo.ColorPalette",
5101
5102     /**
5103      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
5104      */
5105     allowReselect : false,
5106
5107     /**
5108      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
5109      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
5110      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
5111      * of colors with the width setting until the box is symmetrical.</p>
5112      * <p>You can override individual colors if needed:</p>
5113      * <pre><code>
5114 var cp = new Roo.ColorPalette();
5115 cp.colors[0] = "FF0000";  // change the first box to red
5116 </code></pre>
5117
5118 Or you can provide a custom array of your own for complete control:
5119 <pre><code>
5120 var cp = new Roo.ColorPalette();
5121 cp.colors = ["000000", "993300", "333300"];
5122 </code></pre>
5123      * @type Array
5124      */
5125     colors : [
5126         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
5127         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
5128         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
5129         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
5130         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
5131     ],
5132
5133     // private
5134     onRender : function(container, position){
5135         var t = new Roo.MasterTemplate(
5136             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
5137         );
5138         var c = this.colors;
5139         for(var i = 0, len = c.length; i < len; i++){
5140             t.add([c[i]]);
5141         }
5142         var el = document.createElement("div");
5143         el.className = this.itemCls;
5144         t.overwrite(el);
5145         container.dom.insertBefore(el, position);
5146         this.el = Roo.get(el);
5147         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
5148         if(this.clickEvent != 'click'){
5149             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
5150         }
5151     },
5152
5153     // private
5154     afterRender : function(){
5155         Roo.ColorPalette.superclass.afterRender.call(this);
5156         if(this.value){
5157             var s = this.value;
5158             this.value = null;
5159             this.select(s);
5160         }
5161     },
5162
5163     // private
5164     handleClick : function(e, t){
5165         e.preventDefault();
5166         if(!this.disabled){
5167             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
5168             this.select(c.toUpperCase());
5169         }
5170     },
5171
5172     /**
5173      * Selects the specified color in the palette (fires the select event)
5174      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
5175      */
5176     select : function(color){
5177         color = color.replace("#", "");
5178         if(color != this.value || this.allowReselect){
5179             var el = this.el;
5180             if(this.value){
5181                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
5182             }
5183             el.child("a.color-"+color).addClass("x-color-palette-sel");
5184             this.value = color;
5185             this.fireEvent("select", this, color);
5186         }
5187     }
5188 });/*
5189  * Based on:
5190  * Ext JS Library 1.1.1
5191  * Copyright(c) 2006-2007, Ext JS, LLC.
5192  *
5193  * Originally Released Under LGPL - original licence link has changed is not relivant.
5194  *
5195  * Fork - LGPL
5196  * <script type="text/javascript">
5197  */
5198  
5199 /**
5200  * @class Roo.DatePicker
5201  * @extends Roo.Component
5202  * Simple date picker class.
5203  * @constructor
5204  * Create a new DatePicker
5205  * @param {Object} config The config object
5206  */
5207 Roo.DatePicker = function(config){
5208     Roo.DatePicker.superclass.constructor.call(this, config);
5209
5210     this.value = config && config.value ?
5211                  config.value.clearTime() : new Date().clearTime();
5212
5213     this.addEvents({
5214         /**
5215              * @event select
5216              * Fires when a date is selected
5217              * @param {DatePicker} this
5218              * @param {Date} date The selected date
5219              */
5220         'select': true,
5221         /**
5222              * @event monthchange
5223              * Fires when the displayed month changes 
5224              * @param {DatePicker} this
5225              * @param {Date} date The selected month
5226              */
5227         'monthchange': true
5228     });
5229
5230     if(this.handler){
5231         this.on("select", this.handler,  this.scope || this);
5232     }
5233     // build the disabledDatesRE
5234     if(!this.disabledDatesRE && this.disabledDates){
5235         var dd = this.disabledDates;
5236         var re = "(?:";
5237         for(var i = 0; i < dd.length; i++){
5238             re += dd[i];
5239             if(i != dd.length-1) {
5240                 re += "|";
5241             }
5242         }
5243         this.disabledDatesRE = new RegExp(re + ")");
5244     }
5245 };
5246
5247 Roo.extend(Roo.DatePicker, Roo.Component, {
5248     /**
5249      * @cfg {String} todayText
5250      * The text to display on the button that selects the current date (defaults to "Today")
5251      */
5252     todayText : "Today",
5253     /**
5254      * @cfg {String} okText
5255      * The text to display on the ok button
5256      */
5257     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
5258     /**
5259      * @cfg {String} cancelText
5260      * The text to display on the cancel button
5261      */
5262     cancelText : "Cancel",
5263     /**
5264      * @cfg {String} todayTip
5265      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
5266      */
5267     todayTip : "{0} (Spacebar)",
5268     /**
5269      * @cfg {Date} minDate
5270      * Minimum allowable date (JavaScript date object, defaults to null)
5271      */
5272     minDate : null,
5273     /**
5274      * @cfg {Date} maxDate
5275      * Maximum allowable date (JavaScript date object, defaults to null)
5276      */
5277     maxDate : null,
5278     /**
5279      * @cfg {String} minText
5280      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
5281      */
5282     minText : "This date is before the minimum date",
5283     /**
5284      * @cfg {String} maxText
5285      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
5286      */
5287     maxText : "This date is after the maximum date",
5288     /**
5289      * @cfg {String} format
5290      * The default date format string which can be overriden for localization support.  The format must be
5291      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
5292      */
5293     format : "m/d/y",
5294     /**
5295      * @cfg {Array} disabledDays
5296      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
5297      */
5298     disabledDays : null,
5299     /**
5300      * @cfg {String} disabledDaysText
5301      * The tooltip to display when the date falls on a disabled day (defaults to "")
5302      */
5303     disabledDaysText : "",
5304     /**
5305      * @cfg {RegExp} disabledDatesRE
5306      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
5307      */
5308     disabledDatesRE : null,
5309     /**
5310      * @cfg {String} disabledDatesText
5311      * The tooltip text to display when the date falls on a disabled date (defaults to "")
5312      */
5313     disabledDatesText : "",
5314     /**
5315      * @cfg {Boolean} constrainToViewport
5316      * True to constrain the date picker to the viewport (defaults to true)
5317      */
5318     constrainToViewport : true,
5319     /**
5320      * @cfg {Array} monthNames
5321      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
5322      */
5323     monthNames : Date.monthNames,
5324     /**
5325      * @cfg {Array} dayNames
5326      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
5327      */
5328     dayNames : Date.dayNames,
5329     /**
5330      * @cfg {String} nextText
5331      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
5332      */
5333     nextText: 'Next Month (Control+Right)',
5334     /**
5335      * @cfg {String} prevText
5336      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
5337      */
5338     prevText: 'Previous Month (Control+Left)',
5339     /**
5340      * @cfg {String} monthYearText
5341      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
5342      */
5343     monthYearText: 'Choose a month (Control+Up/Down to move years)',
5344     /**
5345      * @cfg {Number} startDay
5346      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
5347      */
5348     startDay : 0,
5349     /**
5350      * @cfg {Bool} showClear
5351      * Show a clear button (usefull for date form elements that can be blank.)
5352      */
5353     
5354     showClear: false,
5355     
5356     /**
5357      * Sets the value of the date field
5358      * @param {Date} value The date to set
5359      */
5360     setValue : function(value){
5361         var old = this.value;
5362         
5363         if (typeof(value) == 'string') {
5364          
5365             value = Date.parseDate(value, this.format);
5366         }
5367         if (!value) {
5368             value = new Date();
5369         }
5370         
5371         this.value = value.clearTime(true);
5372         if(this.el){
5373             this.update(this.value);
5374         }
5375     },
5376
5377     /**
5378      * Gets the current selected value of the date field
5379      * @return {Date} The selected date
5380      */
5381     getValue : function(){
5382         return this.value;
5383     },
5384
5385     // private
5386     focus : function(){
5387         if(this.el){
5388             this.update(this.activeDate);
5389         }
5390     },
5391
5392     // privateval
5393     onRender : function(container, position){
5394         
5395         var m = [
5396              '<table cellspacing="0">',
5397                 '<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>',
5398                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
5399         var dn = this.dayNames;
5400         for(var i = 0; i < 7; i++){
5401             var d = this.startDay+i;
5402             if(d > 6){
5403                 d = d-7;
5404             }
5405             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
5406         }
5407         m[m.length] = "</tr></thead><tbody><tr>";
5408         for(var i = 0; i < 42; i++) {
5409             if(i % 7 == 0 && i != 0){
5410                 m[m.length] = "</tr><tr>";
5411             }
5412             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
5413         }
5414         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
5415             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
5416
5417         var el = document.createElement("div");
5418         el.className = "x-date-picker";
5419         el.innerHTML = m.join("");
5420
5421         container.dom.insertBefore(el, position);
5422
5423         this.el = Roo.get(el);
5424         this.eventEl = Roo.get(el.firstChild);
5425
5426         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
5427             handler: this.showPrevMonth,
5428             scope: this,
5429             preventDefault:true,
5430             stopDefault:true
5431         });
5432
5433         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
5434             handler: this.showNextMonth,
5435             scope: this,
5436             preventDefault:true,
5437             stopDefault:true
5438         });
5439
5440         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
5441
5442         this.monthPicker = this.el.down('div.x-date-mp');
5443         this.monthPicker.enableDisplayMode('block');
5444         
5445         var kn = new Roo.KeyNav(this.eventEl, {
5446             "left" : function(e){
5447                 e.ctrlKey ?
5448                     this.showPrevMonth() :
5449                     this.update(this.activeDate.add("d", -1));
5450             },
5451
5452             "right" : function(e){
5453                 e.ctrlKey ?
5454                     this.showNextMonth() :
5455                     this.update(this.activeDate.add("d", 1));
5456             },
5457
5458             "up" : function(e){
5459                 e.ctrlKey ?
5460                     this.showNextYear() :
5461                     this.update(this.activeDate.add("d", -7));
5462             },
5463
5464             "down" : function(e){
5465                 e.ctrlKey ?
5466                     this.showPrevYear() :
5467                     this.update(this.activeDate.add("d", 7));
5468             },
5469
5470             "pageUp" : function(e){
5471                 this.showNextMonth();
5472             },
5473
5474             "pageDown" : function(e){
5475                 this.showPrevMonth();
5476             },
5477
5478             "enter" : function(e){
5479                 e.stopPropagation();
5480                 return true;
5481             },
5482
5483             scope : this
5484         });
5485
5486         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
5487
5488         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
5489
5490         this.el.unselectable();
5491         
5492         this.cells = this.el.select("table.x-date-inner tbody td");
5493         this.textNodes = this.el.query("table.x-date-inner tbody span");
5494
5495         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
5496             text: "&#160;",
5497             tooltip: this.monthYearText
5498         });
5499
5500         this.mbtn.on('click', this.showMonthPicker, this);
5501         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
5502
5503
5504         var today = (new Date()).dateFormat(this.format);
5505         
5506         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
5507         if (this.showClear) {
5508             baseTb.add( new Roo.Toolbar.Fill());
5509         }
5510         baseTb.add({
5511             text: String.format(this.todayText, today),
5512             tooltip: String.format(this.todayTip, today),
5513             handler: this.selectToday,
5514             scope: this
5515         });
5516         
5517         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
5518             
5519         //});
5520         if (this.showClear) {
5521             
5522             baseTb.add( new Roo.Toolbar.Fill());
5523             baseTb.add({
5524                 text: '&#160;',
5525                 cls: 'x-btn-icon x-btn-clear',
5526                 handler: function() {
5527                     //this.value = '';
5528                     this.fireEvent("select", this, '');
5529                 },
5530                 scope: this
5531             });
5532         }
5533         
5534         
5535         if(Roo.isIE){
5536             this.el.repaint();
5537         }
5538         this.update(this.value);
5539     },
5540
5541     createMonthPicker : function(){
5542         if(!this.monthPicker.dom.firstChild){
5543             var buf = ['<table border="0" cellspacing="0">'];
5544             for(var i = 0; i < 6; i++){
5545                 buf.push(
5546                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
5547                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
5548                     i == 0 ?
5549                     '<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>' :
5550                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
5551                 );
5552             }
5553             buf.push(
5554                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
5555                     this.okText,
5556                     '</button><button type="button" class="x-date-mp-cancel">',
5557                     this.cancelText,
5558                     '</button></td></tr>',
5559                 '</table>'
5560             );
5561             this.monthPicker.update(buf.join(''));
5562             this.monthPicker.on('click', this.onMonthClick, this);
5563             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
5564
5565             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
5566             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
5567
5568             this.mpMonths.each(function(m, a, i){
5569                 i += 1;
5570                 if((i%2) == 0){
5571                     m.dom.xmonth = 5 + Math.round(i * .5);
5572                 }else{
5573                     m.dom.xmonth = Math.round((i-1) * .5);
5574                 }
5575             });
5576         }
5577     },
5578
5579     showMonthPicker : function(){
5580         this.createMonthPicker();
5581         var size = this.el.getSize();
5582         this.monthPicker.setSize(size);
5583         this.monthPicker.child('table').setSize(size);
5584
5585         this.mpSelMonth = (this.activeDate || this.value).getMonth();
5586         this.updateMPMonth(this.mpSelMonth);
5587         this.mpSelYear = (this.activeDate || this.value).getFullYear();
5588         this.updateMPYear(this.mpSelYear);
5589
5590         this.monthPicker.slideIn('t', {duration:.2});
5591     },
5592
5593     updateMPYear : function(y){
5594         this.mpyear = y;
5595         var ys = this.mpYears.elements;
5596         for(var i = 1; i <= 10; i++){
5597             var td = ys[i-1], y2;
5598             if((i%2) == 0){
5599                 y2 = y + Math.round(i * .5);
5600                 td.firstChild.innerHTML = y2;
5601                 td.xyear = y2;
5602             }else{
5603                 y2 = y - (5-Math.round(i * .5));
5604                 td.firstChild.innerHTML = y2;
5605                 td.xyear = y2;
5606             }
5607             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
5608         }
5609     },
5610
5611     updateMPMonth : function(sm){
5612         this.mpMonths.each(function(m, a, i){
5613             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
5614         });
5615     },
5616
5617     selectMPMonth: function(m){
5618         
5619     },
5620
5621     onMonthClick : function(e, t){
5622         e.stopEvent();
5623         var el = new Roo.Element(t), pn;
5624         if(el.is('button.x-date-mp-cancel')){
5625             this.hideMonthPicker();
5626         }
5627         else if(el.is('button.x-date-mp-ok')){
5628             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5629             this.hideMonthPicker();
5630         }
5631         else if(pn = el.up('td.x-date-mp-month', 2)){
5632             this.mpMonths.removeClass('x-date-mp-sel');
5633             pn.addClass('x-date-mp-sel');
5634             this.mpSelMonth = pn.dom.xmonth;
5635         }
5636         else if(pn = el.up('td.x-date-mp-year', 2)){
5637             this.mpYears.removeClass('x-date-mp-sel');
5638             pn.addClass('x-date-mp-sel');
5639             this.mpSelYear = pn.dom.xyear;
5640         }
5641         else if(el.is('a.x-date-mp-prev')){
5642             this.updateMPYear(this.mpyear-10);
5643         }
5644         else if(el.is('a.x-date-mp-next')){
5645             this.updateMPYear(this.mpyear+10);
5646         }
5647     },
5648
5649     onMonthDblClick : function(e, t){
5650         e.stopEvent();
5651         var el = new Roo.Element(t), pn;
5652         if(pn = el.up('td.x-date-mp-month', 2)){
5653             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
5654             this.hideMonthPicker();
5655         }
5656         else if(pn = el.up('td.x-date-mp-year', 2)){
5657             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5658             this.hideMonthPicker();
5659         }
5660     },
5661
5662     hideMonthPicker : function(disableAnim){
5663         if(this.monthPicker){
5664             if(disableAnim === true){
5665                 this.monthPicker.hide();
5666             }else{
5667                 this.monthPicker.slideOut('t', {duration:.2});
5668             }
5669         }
5670     },
5671
5672     // private
5673     showPrevMonth : function(e){
5674         this.update(this.activeDate.add("mo", -1));
5675     },
5676
5677     // private
5678     showNextMonth : function(e){
5679         this.update(this.activeDate.add("mo", 1));
5680     },
5681
5682     // private
5683     showPrevYear : function(){
5684         this.update(this.activeDate.add("y", -1));
5685     },
5686
5687     // private
5688     showNextYear : function(){
5689         this.update(this.activeDate.add("y", 1));
5690     },
5691
5692     // private
5693     handleMouseWheel : function(e){
5694         var delta = e.getWheelDelta();
5695         if(delta > 0){
5696             this.showPrevMonth();
5697             e.stopEvent();
5698         } else if(delta < 0){
5699             this.showNextMonth();
5700             e.stopEvent();
5701         }
5702     },
5703
5704     // private
5705     handleDateClick : function(e, t){
5706         e.stopEvent();
5707         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
5708             this.setValue(new Date(t.dateValue));
5709             this.fireEvent("select", this, this.value);
5710         }
5711     },
5712
5713     // private
5714     selectToday : function(){
5715         this.setValue(new Date().clearTime());
5716         this.fireEvent("select", this, this.value);
5717     },
5718
5719     // private
5720     update : function(date)
5721     {
5722         var vd = this.activeDate;
5723         this.activeDate = date;
5724         if(vd && this.el){
5725             var t = date.getTime();
5726             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
5727                 this.cells.removeClass("x-date-selected");
5728                 this.cells.each(function(c){
5729                    if(c.dom.firstChild.dateValue == t){
5730                        c.addClass("x-date-selected");
5731                        setTimeout(function(){
5732                             try{c.dom.firstChild.focus();}catch(e){}
5733                        }, 50);
5734                        return false;
5735                    }
5736                 });
5737                 return;
5738             }
5739         }
5740         
5741         var days = date.getDaysInMonth();
5742         var firstOfMonth = date.getFirstDateOfMonth();
5743         var startingPos = firstOfMonth.getDay()-this.startDay;
5744
5745         if(startingPos <= this.startDay){
5746             startingPos += 7;
5747         }
5748
5749         var pm = date.add("mo", -1);
5750         var prevStart = pm.getDaysInMonth()-startingPos;
5751
5752         var cells = this.cells.elements;
5753         var textEls = this.textNodes;
5754         days += startingPos;
5755
5756         // convert everything to numbers so it's fast
5757         var day = 86400000;
5758         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
5759         var today = new Date().clearTime().getTime();
5760         var sel = date.clearTime().getTime();
5761         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
5762         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
5763         var ddMatch = this.disabledDatesRE;
5764         var ddText = this.disabledDatesText;
5765         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
5766         var ddaysText = this.disabledDaysText;
5767         var format = this.format;
5768
5769         var setCellClass = function(cal, cell){
5770             cell.title = "";
5771             var t = d.getTime();
5772             cell.firstChild.dateValue = t;
5773             if(t == today){
5774                 cell.className += " x-date-today";
5775                 cell.title = cal.todayText;
5776             }
5777             if(t == sel){
5778                 cell.className += " x-date-selected";
5779                 setTimeout(function(){
5780                     try{cell.firstChild.focus();}catch(e){}
5781                 }, 50);
5782             }
5783             // disabling
5784             if(t < min) {
5785                 cell.className = " x-date-disabled";
5786                 cell.title = cal.minText;
5787                 return;
5788             }
5789             if(t > max) {
5790                 cell.className = " x-date-disabled";
5791                 cell.title = cal.maxText;
5792                 return;
5793             }
5794             if(ddays){
5795                 if(ddays.indexOf(d.getDay()) != -1){
5796                     cell.title = ddaysText;
5797                     cell.className = " x-date-disabled";
5798                 }
5799             }
5800             if(ddMatch && format){
5801                 var fvalue = d.dateFormat(format);
5802                 if(ddMatch.test(fvalue)){
5803                     cell.title = ddText.replace("%0", fvalue);
5804                     cell.className = " x-date-disabled";
5805                 }
5806             }
5807         };
5808
5809         var i = 0;
5810         for(; i < startingPos; i++) {
5811             textEls[i].innerHTML = (++prevStart);
5812             d.setDate(d.getDate()+1);
5813             cells[i].className = "x-date-prevday";
5814             setCellClass(this, cells[i]);
5815         }
5816         for(; i < days; i++){
5817             intDay = i - startingPos + 1;
5818             textEls[i].innerHTML = (intDay);
5819             d.setDate(d.getDate()+1);
5820             cells[i].className = "x-date-active";
5821             setCellClass(this, cells[i]);
5822         }
5823         var extraDays = 0;
5824         for(; i < 42; i++) {
5825              textEls[i].innerHTML = (++extraDays);
5826              d.setDate(d.getDate()+1);
5827              cells[i].className = "x-date-nextday";
5828              setCellClass(this, cells[i]);
5829         }
5830
5831         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
5832         this.fireEvent('monthchange', this, date);
5833         
5834         if(!this.internalRender){
5835             var main = this.el.dom.firstChild;
5836             var w = main.offsetWidth;
5837             this.el.setWidth(w + this.el.getBorderWidth("lr"));
5838             Roo.fly(main).setWidth(w);
5839             this.internalRender = true;
5840             // opera does not respect the auto grow header center column
5841             // then, after it gets a width opera refuses to recalculate
5842             // without a second pass
5843             if(Roo.isOpera && !this.secondPass){
5844                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
5845                 this.secondPass = true;
5846                 this.update.defer(10, this, [date]);
5847             }
5848         }
5849         
5850         
5851     }
5852 });        /*
5853  * Based on:
5854  * Ext JS Library 1.1.1
5855  * Copyright(c) 2006-2007, Ext JS, LLC.
5856  *
5857  * Originally Released Under LGPL - original licence link has changed is not relivant.
5858  *
5859  * Fork - LGPL
5860  * <script type="text/javascript">
5861  */
5862 /**
5863  * @class Roo.TabPanel
5864  * @extends Roo.util.Observable
5865  * A lightweight tab container.
5866  * <br><br>
5867  * Usage:
5868  * <pre><code>
5869 // basic tabs 1, built from existing content
5870 var tabs = new Roo.TabPanel("tabs1");
5871 tabs.addTab("script", "View Script");
5872 tabs.addTab("markup", "View Markup");
5873 tabs.activate("script");
5874
5875 // more advanced tabs, built from javascript
5876 var jtabs = new Roo.TabPanel("jtabs");
5877 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
5878
5879 // set up the UpdateManager
5880 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
5881 var updater = tab2.getUpdateManager();
5882 updater.setDefaultUrl("ajax1.htm");
5883 tab2.on('activate', updater.refresh, updater, true);
5884
5885 // Use setUrl for Ajax loading
5886 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
5887 tab3.setUrl("ajax2.htm", null, true);
5888
5889 // Disabled tab
5890 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
5891 tab4.disable();
5892
5893 jtabs.activate("jtabs-1");
5894  * </code></pre>
5895  * @constructor
5896  * Create a new TabPanel.
5897  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
5898  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
5899  */
5900 Roo.TabPanel = function(container, config){
5901     /**
5902     * The container element for this TabPanel.
5903     * @type Roo.Element
5904     */
5905     this.el = Roo.get(container, true);
5906     if(config){
5907         if(typeof config == "boolean"){
5908             this.tabPosition = config ? "bottom" : "top";
5909         }else{
5910             Roo.apply(this, config);
5911         }
5912     }
5913     if(this.tabPosition == "bottom"){
5914         this.bodyEl = Roo.get(this.createBody(this.el.dom));
5915         this.el.addClass("x-tabs-bottom");
5916     }
5917     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
5918     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
5919     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
5920     if(Roo.isIE){
5921         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
5922     }
5923     if(this.tabPosition != "bottom"){
5924         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
5925          * @type Roo.Element
5926          */
5927         this.bodyEl = Roo.get(this.createBody(this.el.dom));
5928         this.el.addClass("x-tabs-top");
5929     }
5930     this.items = [];
5931
5932     this.bodyEl.setStyle("position", "relative");
5933
5934     this.active = null;
5935     this.activateDelegate = this.activate.createDelegate(this);
5936
5937     this.addEvents({
5938         /**
5939          * @event tabchange
5940          * Fires when the active tab changes
5941          * @param {Roo.TabPanel} this
5942          * @param {Roo.TabPanelItem} activePanel The new active tab
5943          */
5944         "tabchange": true,
5945         /**
5946          * @event beforetabchange
5947          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
5948          * @param {Roo.TabPanel} this
5949          * @param {Object} e Set cancel to true on this object to cancel the tab change
5950          * @param {Roo.TabPanelItem} tab The tab being changed to
5951          */
5952         "beforetabchange" : true
5953     });
5954
5955     Roo.EventManager.onWindowResize(this.onResize, this);
5956     this.cpad = this.el.getPadding("lr");
5957     this.hiddenCount = 0;
5958
5959
5960     // toolbar on the tabbar support...
5961     if (this.toolbar) {
5962         var tcfg = this.toolbar;
5963         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
5964         this.toolbar = new Roo.Toolbar(tcfg);
5965         if (Roo.isSafari) {
5966             var tbl = tcfg.container.child('table', true);
5967             tbl.setAttribute('width', '100%');
5968         }
5969         
5970     }
5971    
5972
5973
5974     Roo.TabPanel.superclass.constructor.call(this);
5975 };
5976
5977 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
5978     /*
5979      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
5980      */
5981     tabPosition : "top",
5982     /*
5983      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
5984      */
5985     currentTabWidth : 0,
5986     /*
5987      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
5988      */
5989     minTabWidth : 40,
5990     /*
5991      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
5992      */
5993     maxTabWidth : 250,
5994     /*
5995      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
5996      */
5997     preferredTabWidth : 175,
5998     /*
5999      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
6000      */
6001     resizeTabs : false,
6002     /*
6003      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
6004      */
6005     monitorResize : true,
6006     /*
6007      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
6008      */
6009     toolbar : false,
6010
6011     /**
6012      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
6013      * @param {String} id The id of the div to use <b>or create</b>
6014      * @param {String} text The text for the tab
6015      * @param {String} content (optional) Content to put in the TabPanelItem body
6016      * @param {Boolean} closable (optional) True to create a close icon on the tab
6017      * @return {Roo.TabPanelItem} The created TabPanelItem
6018      */
6019     addTab : function(id, text, content, closable){
6020         var item = new Roo.TabPanelItem(this, id, text, closable);
6021         this.addTabItem(item);
6022         if(content){
6023             item.setContent(content);
6024         }
6025         return item;
6026     },
6027
6028     /**
6029      * Returns the {@link Roo.TabPanelItem} with the specified id/index
6030      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
6031      * @return {Roo.TabPanelItem}
6032      */
6033     getTab : function(id){
6034         return this.items[id];
6035     },
6036
6037     /**
6038      * Hides the {@link Roo.TabPanelItem} with the specified id/index
6039      * @param {String/Number} id The id or index of the TabPanelItem to hide.
6040      */
6041     hideTab : function(id){
6042         var t = this.items[id];
6043         if(!t.isHidden()){
6044            t.setHidden(true);
6045            this.hiddenCount++;
6046            this.autoSizeTabs();
6047         }
6048     },
6049
6050     /**
6051      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
6052      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
6053      */
6054     unhideTab : function(id){
6055         var t = this.items[id];
6056         if(t.isHidden()){
6057            t.setHidden(false);
6058            this.hiddenCount--;
6059            this.autoSizeTabs();
6060         }
6061     },
6062
6063     /**
6064      * Adds an existing {@link Roo.TabPanelItem}.
6065      * @param {Roo.TabPanelItem} item The TabPanelItem to add
6066      */
6067     addTabItem : function(item){
6068         this.items[item.id] = item;
6069         this.items.push(item);
6070         if(this.resizeTabs){
6071            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
6072            this.autoSizeTabs();
6073         }else{
6074             item.autoSize();
6075         }
6076     },
6077
6078     /**
6079      * Removes a {@link Roo.TabPanelItem}.
6080      * @param {String/Number} id The id or index of the TabPanelItem to remove.
6081      */
6082     removeTab : function(id){
6083         var items = this.items;
6084         var tab = items[id];
6085         if(!tab) { return; }
6086         var index = items.indexOf(tab);
6087         if(this.active == tab && items.length > 1){
6088             var newTab = this.getNextAvailable(index);
6089             if(newTab) {
6090                 newTab.activate();
6091             }
6092         }
6093         this.stripEl.dom.removeChild(tab.pnode.dom);
6094         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
6095             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
6096         }
6097         items.splice(index, 1);
6098         delete this.items[tab.id];
6099         tab.fireEvent("close", tab);
6100         tab.purgeListeners();
6101         this.autoSizeTabs();
6102     },
6103
6104     getNextAvailable : function(start){
6105         var items = this.items;
6106         var index = start;
6107         // look for a next tab that will slide over to
6108         // replace the one being removed
6109         while(index < items.length){
6110             var item = items[++index];
6111             if(item && !item.isHidden()){
6112                 return item;
6113             }
6114         }
6115         // if one isn't found select the previous tab (on the left)
6116         index = start;
6117         while(index >= 0){
6118             var item = items[--index];
6119             if(item && !item.isHidden()){
6120                 return item;
6121             }
6122         }
6123         return null;
6124     },
6125
6126     /**
6127      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
6128      * @param {String/Number} id The id or index of the TabPanelItem to disable.
6129      */
6130     disableTab : function(id){
6131         var tab = this.items[id];
6132         if(tab && this.active != tab){
6133             tab.disable();
6134         }
6135     },
6136
6137     /**
6138      * Enables a {@link Roo.TabPanelItem} that is disabled.
6139      * @param {String/Number} id The id or index of the TabPanelItem to enable.
6140      */
6141     enableTab : function(id){
6142         var tab = this.items[id];
6143         tab.enable();
6144     },
6145
6146     /**
6147      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
6148      * @param {String/Number} id The id or index of the TabPanelItem to activate.
6149      * @return {Roo.TabPanelItem} The TabPanelItem.
6150      */
6151     activate : function(id){
6152         var tab = this.items[id];
6153         if(!tab){
6154             return null;
6155         }
6156         if(tab == this.active || tab.disabled){
6157             return tab;
6158         }
6159         var e = {};
6160         this.fireEvent("beforetabchange", this, e, tab);
6161         if(e.cancel !== true && !tab.disabled){
6162             if(this.active){
6163                 this.active.hide();
6164             }
6165             this.active = this.items[id];
6166             this.active.show();
6167             this.fireEvent("tabchange", this, this.active);
6168         }
6169         return tab;
6170     },
6171
6172     /**
6173      * Gets the active {@link Roo.TabPanelItem}.
6174      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
6175      */
6176     getActiveTab : function(){
6177         return this.active;
6178     },
6179
6180     /**
6181      * Updates the tab body element to fit the height of the container element
6182      * for overflow scrolling
6183      * @param {Number} targetHeight (optional) Override the starting height from the elements height
6184      */
6185     syncHeight : function(targetHeight){
6186         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
6187         var bm = this.bodyEl.getMargins();
6188         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
6189         this.bodyEl.setHeight(newHeight);
6190         return newHeight;
6191     },
6192
6193     onResize : function(){
6194         if(this.monitorResize){
6195             this.autoSizeTabs();
6196         }
6197     },
6198
6199     /**
6200      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
6201      */
6202     beginUpdate : function(){
6203         this.updating = true;
6204     },
6205
6206     /**
6207      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
6208      */
6209     endUpdate : function(){
6210         this.updating = false;
6211         this.autoSizeTabs();
6212     },
6213
6214     /**
6215      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
6216      */
6217     autoSizeTabs : function(){
6218         var count = this.items.length;
6219         var vcount = count - this.hiddenCount;
6220         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
6221             return;
6222         }
6223         var w = Math.max(this.el.getWidth() - this.cpad, 10);
6224         var availWidth = Math.floor(w / vcount);
6225         var b = this.stripBody;
6226         if(b.getWidth() > w){
6227             var tabs = this.items;
6228             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
6229             if(availWidth < this.minTabWidth){
6230                 /*if(!this.sleft){    // incomplete scrolling code
6231                     this.createScrollButtons();
6232                 }
6233                 this.showScroll();
6234                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
6235             }
6236         }else{
6237             if(this.currentTabWidth < this.preferredTabWidth){
6238                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
6239             }
6240         }
6241     },
6242
6243     /**
6244      * Returns the number of tabs in this TabPanel.
6245      * @return {Number}
6246      */
6247      getCount : function(){
6248          return this.items.length;
6249      },
6250
6251     /**
6252      * Resizes all the tabs to the passed width
6253      * @param {Number} The new width
6254      */
6255     setTabWidth : function(width){
6256         this.currentTabWidth = width;
6257         for(var i = 0, len = this.items.length; i < len; i++) {
6258                 if(!this.items[i].isHidden()) {
6259                 this.items[i].setWidth(width);
6260             }
6261         }
6262     },
6263
6264     /**
6265      * Destroys this TabPanel
6266      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
6267      */
6268     destroy : function(removeEl){
6269         Roo.EventManager.removeResizeListener(this.onResize, this);
6270         for(var i = 0, len = this.items.length; i < len; i++){
6271             this.items[i].purgeListeners();
6272         }
6273         if(removeEl === true){
6274             this.el.update("");
6275             this.el.remove();
6276         }
6277     }
6278 });
6279
6280 /**
6281  * @class Roo.TabPanelItem
6282  * @extends Roo.util.Observable
6283  * Represents an individual item (tab plus body) in a TabPanel.
6284  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
6285  * @param {String} id The id of this TabPanelItem
6286  * @param {String} text The text for the tab of this TabPanelItem
6287  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
6288  */
6289 Roo.TabPanelItem = function(tabPanel, id, text, closable){
6290     /**
6291      * The {@link Roo.TabPanel} this TabPanelItem belongs to
6292      * @type Roo.TabPanel
6293      */
6294     this.tabPanel = tabPanel;
6295     /**
6296      * The id for this TabPanelItem
6297      * @type String
6298      */
6299     this.id = id;
6300     /** @private */
6301     this.disabled = false;
6302     /** @private */
6303     this.text = text;
6304     /** @private */
6305     this.loaded = false;
6306     this.closable = closable;
6307
6308     /**
6309      * The body element for this TabPanelItem.
6310      * @type Roo.Element
6311      */
6312     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
6313     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
6314     this.bodyEl.setStyle("display", "block");
6315     this.bodyEl.setStyle("zoom", "1");
6316     this.hideAction();
6317
6318     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
6319     /** @private */
6320     this.el = Roo.get(els.el, true);
6321     this.inner = Roo.get(els.inner, true);
6322     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
6323     this.pnode = Roo.get(els.el.parentNode, true);
6324     this.el.on("mousedown", this.onTabMouseDown, this);
6325     this.el.on("click", this.onTabClick, this);
6326     /** @private */
6327     if(closable){
6328         var c = Roo.get(els.close, true);
6329         c.dom.title = this.closeText;
6330         c.addClassOnOver("close-over");
6331         c.on("click", this.closeClick, this);
6332      }
6333
6334     this.addEvents({
6335          /**
6336          * @event activate
6337          * Fires when this tab becomes the active tab.
6338          * @param {Roo.TabPanel} tabPanel The parent TabPanel
6339          * @param {Roo.TabPanelItem} this
6340          */
6341         "activate": true,
6342         /**
6343          * @event beforeclose
6344          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
6345          * @param {Roo.TabPanelItem} this
6346          * @param {Object} e Set cancel to true on this object to cancel the close.
6347          */
6348         "beforeclose": true,
6349         /**
6350          * @event close
6351          * Fires when this tab is closed.
6352          * @param {Roo.TabPanelItem} this
6353          */
6354          "close": true,
6355         /**
6356          * @event deactivate
6357          * Fires when this tab is no longer the active tab.
6358          * @param {Roo.TabPanel} tabPanel The parent TabPanel
6359          * @param {Roo.TabPanelItem} this
6360          */
6361          "deactivate" : true
6362     });
6363     this.hidden = false;
6364
6365     Roo.TabPanelItem.superclass.constructor.call(this);
6366 };
6367
6368 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
6369     purgeListeners : function(){
6370        Roo.util.Observable.prototype.purgeListeners.call(this);
6371        this.el.removeAllListeners();
6372     },
6373     /**
6374      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
6375      */
6376     show : function(){
6377         this.pnode.addClass("on");
6378         this.showAction();
6379         if(Roo.isOpera){
6380             this.tabPanel.stripWrap.repaint();
6381         }
6382         this.fireEvent("activate", this.tabPanel, this);
6383     },
6384
6385     /**
6386      * Returns true if this tab is the active tab.
6387      * @return {Boolean}
6388      */
6389     isActive : function(){
6390         return this.tabPanel.getActiveTab() == this;
6391     },
6392
6393     /**
6394      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
6395      */
6396     hide : function(){
6397         this.pnode.removeClass("on");
6398         this.hideAction();
6399         this.fireEvent("deactivate", this.tabPanel, this);
6400     },
6401
6402     hideAction : function(){
6403         this.bodyEl.hide();
6404         this.bodyEl.setStyle("position", "absolute");
6405         this.bodyEl.setLeft("-20000px");
6406         this.bodyEl.setTop("-20000px");
6407     },
6408
6409     showAction : function(){
6410         this.bodyEl.setStyle("position", "relative");
6411         this.bodyEl.setTop("");
6412         this.bodyEl.setLeft("");
6413         this.bodyEl.show();
6414     },
6415
6416     /**
6417      * Set the tooltip for the tab.
6418      * @param {String} tooltip The tab's tooltip
6419      */
6420     setTooltip : function(text){
6421         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
6422             this.textEl.dom.qtip = text;
6423             this.textEl.dom.removeAttribute('title');
6424         }else{
6425             this.textEl.dom.title = text;
6426         }
6427     },
6428
6429     onTabClick : function(e){
6430         e.preventDefault();
6431         this.tabPanel.activate(this.id);
6432     },
6433
6434     onTabMouseDown : function(e){
6435         e.preventDefault();
6436         this.tabPanel.activate(this.id);
6437     },
6438
6439     getWidth : function(){
6440         return this.inner.getWidth();
6441     },
6442
6443     setWidth : function(width){
6444         var iwidth = width - this.pnode.getPadding("lr");
6445         this.inner.setWidth(iwidth);
6446         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
6447         this.pnode.setWidth(width);
6448     },
6449
6450     /**
6451      * Show or hide the tab
6452      * @param {Boolean} hidden True to hide or false to show.
6453      */
6454     setHidden : function(hidden){
6455         this.hidden = hidden;
6456         this.pnode.setStyle("display", hidden ? "none" : "");
6457     },
6458
6459     /**
6460      * Returns true if this tab is "hidden"
6461      * @return {Boolean}
6462      */
6463     isHidden : function(){
6464         return this.hidden;
6465     },
6466
6467     /**
6468      * Returns the text for this tab
6469      * @return {String}
6470      */
6471     getText : function(){
6472         return this.text;
6473     },
6474
6475     autoSize : function(){
6476         //this.el.beginMeasure();
6477         this.textEl.setWidth(1);
6478         /*
6479          *  #2804 [new] Tabs in Roojs
6480          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
6481          */
6482         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
6483         //this.el.endMeasure();
6484     },
6485
6486     /**
6487      * Sets the text for the tab (Note: this also sets the tooltip text)
6488      * @param {String} text The tab's text and tooltip
6489      */
6490     setText : function(text){
6491         this.text = text;
6492         this.textEl.update(text);
6493         this.setTooltip(text);
6494         if(!this.tabPanel.resizeTabs){
6495             this.autoSize();
6496         }
6497     },
6498     /**
6499      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
6500      */
6501     activate : function(){
6502         this.tabPanel.activate(this.id);
6503     },
6504
6505     /**
6506      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
6507      */
6508     disable : function(){
6509         if(this.tabPanel.active != this){
6510             this.disabled = true;
6511             this.pnode.addClass("disabled");
6512         }
6513     },
6514
6515     /**
6516      * Enables this TabPanelItem if it was previously disabled.
6517      */
6518     enable : function(){
6519         this.disabled = false;
6520         this.pnode.removeClass("disabled");
6521     },
6522
6523     /**
6524      * Sets the content for this TabPanelItem.
6525      * @param {String} content The content
6526      * @param {Boolean} loadScripts true to look for and load scripts
6527      */
6528     setContent : function(content, loadScripts){
6529         this.bodyEl.update(content, loadScripts);
6530     },
6531
6532     /**
6533      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
6534      * @return {Roo.UpdateManager} The UpdateManager
6535      */
6536     getUpdateManager : function(){
6537         return this.bodyEl.getUpdateManager();
6538     },
6539
6540     /**
6541      * Set a URL to be used to load the content for this TabPanelItem.
6542      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
6543      * @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)
6544      * @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)
6545      * @return {Roo.UpdateManager} The UpdateManager
6546      */
6547     setUrl : function(url, params, loadOnce){
6548         if(this.refreshDelegate){
6549             this.un('activate', this.refreshDelegate);
6550         }
6551         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
6552         this.on("activate", this.refreshDelegate);
6553         return this.bodyEl.getUpdateManager();
6554     },
6555
6556     /** @private */
6557     _handleRefresh : function(url, params, loadOnce){
6558         if(!loadOnce || !this.loaded){
6559             var updater = this.bodyEl.getUpdateManager();
6560             updater.update(url, params, this._setLoaded.createDelegate(this));
6561         }
6562     },
6563
6564     /**
6565      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
6566      *   Will fail silently if the setUrl method has not been called.
6567      *   This does not activate the panel, just updates its content.
6568      */
6569     refresh : function(){
6570         if(this.refreshDelegate){
6571            this.loaded = false;
6572            this.refreshDelegate();
6573         }
6574     },
6575
6576     /** @private */
6577     _setLoaded : function(){
6578         this.loaded = true;
6579     },
6580
6581     /** @private */
6582     closeClick : function(e){
6583         var o = {};
6584         e.stopEvent();
6585         this.fireEvent("beforeclose", this, o);
6586         if(o.cancel !== true){
6587             this.tabPanel.removeTab(this.id);
6588         }
6589     },
6590     /**
6591      * The text displayed in the tooltip for the close icon.
6592      * @type String
6593      */
6594     closeText : "Close this tab"
6595 });
6596
6597 /** @private */
6598 Roo.TabPanel.prototype.createStrip = function(container){
6599     var strip = document.createElement("div");
6600     strip.className = "x-tabs-wrap";
6601     container.appendChild(strip);
6602     return strip;
6603 };
6604 /** @private */
6605 Roo.TabPanel.prototype.createStripList = function(strip){
6606     // div wrapper for retard IE
6607     // returns the "tr" element.
6608     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
6609         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
6610         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
6611     return strip.firstChild.firstChild.firstChild.firstChild;
6612 };
6613 /** @private */
6614 Roo.TabPanel.prototype.createBody = function(container){
6615     var body = document.createElement("div");
6616     Roo.id(body, "tab-body");
6617     Roo.fly(body).addClass("x-tabs-body");
6618     container.appendChild(body);
6619     return body;
6620 };
6621 /** @private */
6622 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
6623     var body = Roo.getDom(id);
6624     if(!body){
6625         body = document.createElement("div");
6626         body.id = id;
6627     }
6628     Roo.fly(body).addClass("x-tabs-item-body");
6629     bodyEl.insertBefore(body, bodyEl.firstChild);
6630     return body;
6631 };
6632 /** @private */
6633 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
6634     var td = document.createElement("td");
6635     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
6636     //stripEl.appendChild(td);
6637     if(closable){
6638         td.className = "x-tabs-closable";
6639         if(!this.closeTpl){
6640             this.closeTpl = new Roo.Template(
6641                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6642                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
6643                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
6644             );
6645         }
6646         var el = this.closeTpl.overwrite(td, {"text": text});
6647         var close = el.getElementsByTagName("div")[0];
6648         var inner = el.getElementsByTagName("em")[0];
6649         return {"el": el, "close": close, "inner": inner};
6650     } else {
6651         if(!this.tabTpl){
6652             this.tabTpl = new Roo.Template(
6653                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6654                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
6655             );
6656         }
6657         var el = this.tabTpl.overwrite(td, {"text": text});
6658         var inner = el.getElementsByTagName("em")[0];
6659         return {"el": el, "inner": inner};
6660     }
6661 };/*
6662  * Based on:
6663  * Ext JS Library 1.1.1
6664  * Copyright(c) 2006-2007, Ext JS, LLC.
6665  *
6666  * Originally Released Under LGPL - original licence link has changed is not relivant.
6667  *
6668  * Fork - LGPL
6669  * <script type="text/javascript">
6670  */
6671
6672 /**
6673  * @class Roo.Button
6674  * @extends Roo.util.Observable
6675  * Simple Button class
6676  * @cfg {String} text The button text
6677  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
6678  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
6679  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
6680  * @cfg {Object} scope The scope of the handler
6681  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
6682  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
6683  * @cfg {Boolean} hidden True to start hidden (defaults to false)
6684  * @cfg {Boolean} disabled True to start disabled (defaults to false)
6685  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
6686  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
6687    applies if enableToggle = true)
6688  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
6689  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
6690   an {@link Roo.util.ClickRepeater} config object (defaults to false).
6691  * @constructor
6692  * Create a new button
6693  * @param {Object} config The config object
6694  */
6695 Roo.Button = function(renderTo, config)
6696 {
6697     if (!config) {
6698         config = renderTo;
6699         renderTo = config.renderTo || false;
6700     }
6701     
6702     Roo.apply(this, config);
6703     this.addEvents({
6704         /**
6705              * @event click
6706              * Fires when this button is clicked
6707              * @param {Button} this
6708              * @param {EventObject} e The click event
6709              */
6710             "click" : true,
6711         /**
6712              * @event toggle
6713              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
6714              * @param {Button} this
6715              * @param {Boolean} pressed
6716              */
6717             "toggle" : true,
6718         /**
6719              * @event mouseover
6720              * Fires when the mouse hovers over the button
6721              * @param {Button} this
6722              * @param {Event} e The event object
6723              */
6724         'mouseover' : true,
6725         /**
6726              * @event mouseout
6727              * Fires when the mouse exits the button
6728              * @param {Button} this
6729              * @param {Event} e The event object
6730              */
6731         'mouseout': true,
6732          /**
6733              * @event render
6734              * Fires when the button is rendered
6735              * @param {Button} this
6736              */
6737         'render': true
6738     });
6739     if(this.menu){
6740         this.menu = Roo.menu.MenuMgr.get(this.menu);
6741     }
6742     // register listeners first!!  - so render can be captured..
6743     Roo.util.Observable.call(this);
6744     if(renderTo){
6745         this.render(renderTo);
6746     }
6747     
6748   
6749 };
6750
6751 Roo.extend(Roo.Button, Roo.util.Observable, {
6752     /**
6753      * 
6754      */
6755     
6756     /**
6757      * Read-only. True if this button is hidden
6758      * @type Boolean
6759      */
6760     hidden : false,
6761     /**
6762      * Read-only. True if this button is disabled
6763      * @type Boolean
6764      */
6765     disabled : false,
6766     /**
6767      * Read-only. True if this button is pressed (only if enableToggle = true)
6768      * @type Boolean
6769      */
6770     pressed : false,
6771
6772     /**
6773      * @cfg {Number} tabIndex 
6774      * The DOM tabIndex for this button (defaults to undefined)
6775      */
6776     tabIndex : undefined,
6777
6778     /**
6779      * @cfg {Boolean} enableToggle
6780      * True to enable pressed/not pressed toggling (defaults to false)
6781      */
6782     enableToggle: false,
6783     /**
6784      * @cfg {Mixed} menu
6785      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
6786      */
6787     menu : undefined,
6788     /**
6789      * @cfg {String} menuAlign
6790      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
6791      */
6792     menuAlign : "tl-bl?",
6793
6794     /**
6795      * @cfg {String} iconCls
6796      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
6797      */
6798     iconCls : undefined,
6799     /**
6800      * @cfg {String} type
6801      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
6802      */
6803     type : 'button',
6804
6805     // private
6806     menuClassTarget: 'tr',
6807
6808     /**
6809      * @cfg {String} clickEvent
6810      * The type of event to map to the button's event handler (defaults to 'click')
6811      */
6812     clickEvent : 'click',
6813
6814     /**
6815      * @cfg {Boolean} handleMouseEvents
6816      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
6817      */
6818     handleMouseEvents : true,
6819
6820     /**
6821      * @cfg {String} tooltipType
6822      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
6823      */
6824     tooltipType : 'qtip',
6825
6826     /**
6827      * @cfg {String} cls
6828      * A CSS class to apply to the button's main element.
6829      */
6830     
6831     /**
6832      * @cfg {Roo.Template} template (Optional)
6833      * An {@link Roo.Template} with which to create the Button's main element. This Template must
6834      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
6835      * require code modifications if required elements (e.g. a button) aren't present.
6836      */
6837
6838     // private
6839     render : function(renderTo){
6840         var btn;
6841         if(this.hideParent){
6842             this.parentEl = Roo.get(renderTo);
6843         }
6844         if(!this.dhconfig){
6845             if(!this.template){
6846                 if(!Roo.Button.buttonTemplate){
6847                     // hideous table template
6848                     Roo.Button.buttonTemplate = new Roo.Template(
6849                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
6850                         '<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>',
6851                         "</tr></tbody></table>");
6852                 }
6853                 this.template = Roo.Button.buttonTemplate;
6854             }
6855             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
6856             var btnEl = btn.child("button:first");
6857             btnEl.on('focus', this.onFocus, this);
6858             btnEl.on('blur', this.onBlur, this);
6859             if(this.cls){
6860                 btn.addClass(this.cls);
6861             }
6862             if(this.icon){
6863                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
6864             }
6865             if(this.iconCls){
6866                 btnEl.addClass(this.iconCls);
6867                 if(!this.cls){
6868                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
6869                 }
6870             }
6871             if(this.tabIndex !== undefined){
6872                 btnEl.dom.tabIndex = this.tabIndex;
6873             }
6874             if(this.tooltip){
6875                 if(typeof this.tooltip == 'object'){
6876                     Roo.QuickTips.tips(Roo.apply({
6877                           target: btnEl.id
6878                     }, this.tooltip));
6879                 } else {
6880                     btnEl.dom[this.tooltipType] = this.tooltip;
6881                 }
6882             }
6883         }else{
6884             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
6885         }
6886         this.el = btn;
6887         if(this.id){
6888             this.el.dom.id = this.el.id = this.id;
6889         }
6890         if(this.menu){
6891             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
6892             this.menu.on("show", this.onMenuShow, this);
6893             this.menu.on("hide", this.onMenuHide, this);
6894         }
6895         btn.addClass("x-btn");
6896         if(Roo.isIE && !Roo.isIE7){
6897             this.autoWidth.defer(1, this);
6898         }else{
6899             this.autoWidth();
6900         }
6901         if(this.handleMouseEvents){
6902             btn.on("mouseover", this.onMouseOver, this);
6903             btn.on("mouseout", this.onMouseOut, this);
6904             btn.on("mousedown", this.onMouseDown, this);
6905         }
6906         btn.on(this.clickEvent, this.onClick, this);
6907         //btn.on("mouseup", this.onMouseUp, this);
6908         if(this.hidden){
6909             this.hide();
6910         }
6911         if(this.disabled){
6912             this.disable();
6913         }
6914         Roo.ButtonToggleMgr.register(this);
6915         if(this.pressed){
6916             this.el.addClass("x-btn-pressed");
6917         }
6918         if(this.repeat){
6919             var repeater = new Roo.util.ClickRepeater(btn,
6920                 typeof this.repeat == "object" ? this.repeat : {}
6921             );
6922             repeater.on("click", this.onClick,  this);
6923         }
6924         
6925         this.fireEvent('render', this);
6926         
6927     },
6928     /**
6929      * Returns the button's underlying element
6930      * @return {Roo.Element} The element
6931      */
6932     getEl : function(){
6933         return this.el;  
6934     },
6935     
6936     /**
6937      * Destroys this Button and removes any listeners.
6938      */
6939     destroy : function(){
6940         Roo.ButtonToggleMgr.unregister(this);
6941         this.el.removeAllListeners();
6942         this.purgeListeners();
6943         this.el.remove();
6944     },
6945
6946     // private
6947     autoWidth : function(){
6948         if(this.el){
6949             this.el.setWidth("auto");
6950             if(Roo.isIE7 && Roo.isStrict){
6951                 var ib = this.el.child('button');
6952                 if(ib && ib.getWidth() > 20){
6953                     ib.clip();
6954                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
6955                 }
6956             }
6957             if(this.minWidth){
6958                 if(this.hidden){
6959                     this.el.beginMeasure();
6960                 }
6961                 if(this.el.getWidth() < this.minWidth){
6962                     this.el.setWidth(this.minWidth);
6963                 }
6964                 if(this.hidden){
6965                     this.el.endMeasure();
6966                 }
6967             }
6968         }
6969     },
6970
6971     /**
6972      * Assigns this button's click handler
6973      * @param {Function} handler The function to call when the button is clicked
6974      * @param {Object} scope (optional) Scope for the function passed in
6975      */
6976     setHandler : function(handler, scope){
6977         this.handler = handler;
6978         this.scope = scope;  
6979     },
6980     
6981     /**
6982      * Sets this button's text
6983      * @param {String} text The button text
6984      */
6985     setText : function(text){
6986         this.text = text;
6987         if(this.el){
6988             this.el.child("td.x-btn-center button.x-btn-text").update(text);
6989         }
6990         this.autoWidth();
6991     },
6992     
6993     /**
6994      * Gets the text for this button
6995      * @return {String} The button text
6996      */
6997     getText : function(){
6998         return this.text;  
6999     },
7000     
7001     /**
7002      * Show this button
7003      */
7004     show: function(){
7005         this.hidden = false;
7006         if(this.el){
7007             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
7008         }
7009     },
7010     
7011     /**
7012      * Hide this button
7013      */
7014     hide: function(){
7015         this.hidden = true;
7016         if(this.el){
7017             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
7018         }
7019     },
7020     
7021     /**
7022      * Convenience function for boolean show/hide
7023      * @param {Boolean} visible True to show, false to hide
7024      */
7025     setVisible: function(visible){
7026         if(visible) {
7027             this.show();
7028         }else{
7029             this.hide();
7030         }
7031     },
7032     
7033     /**
7034      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
7035      * @param {Boolean} state (optional) Force a particular state
7036      */
7037     toggle : function(state){
7038         state = state === undefined ? !this.pressed : state;
7039         if(state != this.pressed){
7040             if(state){
7041                 this.el.addClass("x-btn-pressed");
7042                 this.pressed = true;
7043                 this.fireEvent("toggle", this, true);
7044             }else{
7045                 this.el.removeClass("x-btn-pressed");
7046                 this.pressed = false;
7047                 this.fireEvent("toggle", this, false);
7048             }
7049             if(this.toggleHandler){
7050                 this.toggleHandler.call(this.scope || this, this, state);
7051             }
7052         }
7053     },
7054     
7055     /**
7056      * Focus the button
7057      */
7058     focus : function(){
7059         this.el.child('button:first').focus();
7060     },
7061     
7062     /**
7063      * Disable this button
7064      */
7065     disable : function(){
7066         if(this.el){
7067             this.el.addClass("x-btn-disabled");
7068         }
7069         this.disabled = true;
7070     },
7071     
7072     /**
7073      * Enable this button
7074      */
7075     enable : function(){
7076         if(this.el){
7077             this.el.removeClass("x-btn-disabled");
7078         }
7079         this.disabled = false;
7080     },
7081
7082     /**
7083      * Convenience function for boolean enable/disable
7084      * @param {Boolean} enabled True to enable, false to disable
7085      */
7086     setDisabled : function(v){
7087         this[v !== true ? "enable" : "disable"]();
7088     },
7089
7090     // private
7091     onClick : function(e)
7092     {
7093         if(e){
7094             e.preventDefault();
7095         }
7096         if(e.button != 0){
7097             return;
7098         }
7099         if(!this.disabled){
7100             if(this.enableToggle){
7101                 this.toggle();
7102             }
7103             if(this.menu && !this.menu.isVisible()){
7104                 this.menu.show(this.el, this.menuAlign);
7105             }
7106             this.fireEvent("click", this, e);
7107             if(this.handler){
7108                 this.el.removeClass("x-btn-over");
7109                 this.handler.call(this.scope || this, this, e);
7110             }
7111         }
7112     },
7113     // private
7114     onMouseOver : function(e){
7115         if(!this.disabled){
7116             this.el.addClass("x-btn-over");
7117             this.fireEvent('mouseover', this, e);
7118         }
7119     },
7120     // private
7121     onMouseOut : function(e){
7122         if(!e.within(this.el,  true)){
7123             this.el.removeClass("x-btn-over");
7124             this.fireEvent('mouseout', this, e);
7125         }
7126     },
7127     // private
7128     onFocus : function(e){
7129         if(!this.disabled){
7130             this.el.addClass("x-btn-focus");
7131         }
7132     },
7133     // private
7134     onBlur : function(e){
7135         this.el.removeClass("x-btn-focus");
7136     },
7137     // private
7138     onMouseDown : function(e){
7139         if(!this.disabled && e.button == 0){
7140             this.el.addClass("x-btn-click");
7141             Roo.get(document).on('mouseup', this.onMouseUp, this);
7142         }
7143     },
7144     // private
7145     onMouseUp : function(e){
7146         if(e.button == 0){
7147             this.el.removeClass("x-btn-click");
7148             Roo.get(document).un('mouseup', this.onMouseUp, this);
7149         }
7150     },
7151     // private
7152     onMenuShow : function(e){
7153         this.el.addClass("x-btn-menu-active");
7154     },
7155     // private
7156     onMenuHide : function(e){
7157         this.el.removeClass("x-btn-menu-active");
7158     }   
7159 });
7160
7161 // Private utility class used by Button
7162 Roo.ButtonToggleMgr = function(){
7163    var groups = {};
7164    
7165    function toggleGroup(btn, state){
7166        if(state){
7167            var g = groups[btn.toggleGroup];
7168            for(var i = 0, l = g.length; i < l; i++){
7169                if(g[i] != btn){
7170                    g[i].toggle(false);
7171                }
7172            }
7173        }
7174    }
7175    
7176    return {
7177        register : function(btn){
7178            if(!btn.toggleGroup){
7179                return;
7180            }
7181            var g = groups[btn.toggleGroup];
7182            if(!g){
7183                g = groups[btn.toggleGroup] = [];
7184            }
7185            g.push(btn);
7186            btn.on("toggle", toggleGroup);
7187        },
7188        
7189        unregister : function(btn){
7190            if(!btn.toggleGroup){
7191                return;
7192            }
7193            var g = groups[btn.toggleGroup];
7194            if(g){
7195                g.remove(btn);
7196                btn.un("toggle", toggleGroup);
7197            }
7198        }
7199    };
7200 }();/*
7201  * Based on:
7202  * Ext JS Library 1.1.1
7203  * Copyright(c) 2006-2007, Ext JS, LLC.
7204  *
7205  * Originally Released Under LGPL - original licence link has changed is not relivant.
7206  *
7207  * Fork - LGPL
7208  * <script type="text/javascript">
7209  */
7210  
7211 /**
7212  * @class Roo.SplitButton
7213  * @extends Roo.Button
7214  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
7215  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
7216  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
7217  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
7218  * @cfg {String} arrowTooltip The title attribute of the arrow
7219  * @constructor
7220  * Create a new menu button
7221  * @param {String/HTMLElement/Element} renderTo The element to append the button to
7222  * @param {Object} config The config object
7223  */
7224 Roo.SplitButton = function(renderTo, config){
7225     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
7226     /**
7227      * @event arrowclick
7228      * Fires when this button's arrow is clicked
7229      * @param {SplitButton} this
7230      * @param {EventObject} e The click event
7231      */
7232     this.addEvents({"arrowclick":true});
7233 };
7234
7235 Roo.extend(Roo.SplitButton, Roo.Button, {
7236     render : function(renderTo){
7237         // this is one sweet looking template!
7238         var tpl = new Roo.Template(
7239             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
7240             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
7241             '<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>',
7242             "</tbody></table></td><td>",
7243             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
7244             '<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>',
7245             "</tbody></table></td></tr></table>"
7246         );
7247         var btn = tpl.append(renderTo, [this.text, this.type], true);
7248         var btnEl = btn.child("button");
7249         if(this.cls){
7250             btn.addClass(this.cls);
7251         }
7252         if(this.icon){
7253             btnEl.setStyle('background-image', 'url(' +this.icon +')');
7254         }
7255         if(this.iconCls){
7256             btnEl.addClass(this.iconCls);
7257             if(!this.cls){
7258                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
7259             }
7260         }
7261         this.el = btn;
7262         if(this.handleMouseEvents){
7263             btn.on("mouseover", this.onMouseOver, this);
7264             btn.on("mouseout", this.onMouseOut, this);
7265             btn.on("mousedown", this.onMouseDown, this);
7266             btn.on("mouseup", this.onMouseUp, this);
7267         }
7268         btn.on(this.clickEvent, this.onClick, this);
7269         if(this.tooltip){
7270             if(typeof this.tooltip == 'object'){
7271                 Roo.QuickTips.tips(Roo.apply({
7272                       target: btnEl.id
7273                 }, this.tooltip));
7274             } else {
7275                 btnEl.dom[this.tooltipType] = this.tooltip;
7276             }
7277         }
7278         if(this.arrowTooltip){
7279             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
7280         }
7281         if(this.hidden){
7282             this.hide();
7283         }
7284         if(this.disabled){
7285             this.disable();
7286         }
7287         if(this.pressed){
7288             this.el.addClass("x-btn-pressed");
7289         }
7290         if(Roo.isIE && !Roo.isIE7){
7291             this.autoWidth.defer(1, this);
7292         }else{
7293             this.autoWidth();
7294         }
7295         if(this.menu){
7296             this.menu.on("show", this.onMenuShow, this);
7297             this.menu.on("hide", this.onMenuHide, this);
7298         }
7299         this.fireEvent('render', this);
7300     },
7301
7302     // private
7303     autoWidth : function(){
7304         if(this.el){
7305             var tbl = this.el.child("table:first");
7306             var tbl2 = this.el.child("table:last");
7307             this.el.setWidth("auto");
7308             tbl.setWidth("auto");
7309             if(Roo.isIE7 && Roo.isStrict){
7310                 var ib = this.el.child('button:first');
7311                 if(ib && ib.getWidth() > 20){
7312                     ib.clip();
7313                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
7314                 }
7315             }
7316             if(this.minWidth){
7317                 if(this.hidden){
7318                     this.el.beginMeasure();
7319                 }
7320                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
7321                     tbl.setWidth(this.minWidth-tbl2.getWidth());
7322                 }
7323                 if(this.hidden){
7324                     this.el.endMeasure();
7325                 }
7326             }
7327             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
7328         } 
7329     },
7330     /**
7331      * Sets this button's click handler
7332      * @param {Function} handler The function to call when the button is clicked
7333      * @param {Object} scope (optional) Scope for the function passed above
7334      */
7335     setHandler : function(handler, scope){
7336         this.handler = handler;
7337         this.scope = scope;  
7338     },
7339     
7340     /**
7341      * Sets this button's arrow click handler
7342      * @param {Function} handler The function to call when the arrow is clicked
7343      * @param {Object} scope (optional) Scope for the function passed above
7344      */
7345     setArrowHandler : function(handler, scope){
7346         this.arrowHandler = handler;
7347         this.scope = scope;  
7348     },
7349     
7350     /**
7351      * Focus the button
7352      */
7353     focus : function(){
7354         if(this.el){
7355             this.el.child("button:first").focus();
7356         }
7357     },
7358
7359     // private
7360     onClick : function(e){
7361         e.preventDefault();
7362         if(!this.disabled){
7363             if(e.getTarget(".x-btn-menu-arrow-wrap")){
7364                 if(this.menu && !this.menu.isVisible()){
7365                     this.menu.show(this.el, this.menuAlign);
7366                 }
7367                 this.fireEvent("arrowclick", this, e);
7368                 if(this.arrowHandler){
7369                     this.arrowHandler.call(this.scope || this, this, e);
7370                 }
7371             }else{
7372                 this.fireEvent("click", this, e);
7373                 if(this.handler){
7374                     this.handler.call(this.scope || this, this, e);
7375                 }
7376             }
7377         }
7378     },
7379     // private
7380     onMouseDown : function(e){
7381         if(!this.disabled){
7382             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
7383         }
7384     },
7385     // private
7386     onMouseUp : function(e){
7387         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
7388     }   
7389 });
7390
7391
7392 // backwards compat
7393 Roo.MenuButton = Roo.SplitButton;/*
7394  * Based on:
7395  * Ext JS Library 1.1.1
7396  * Copyright(c) 2006-2007, Ext JS, LLC.
7397  *
7398  * Originally Released Under LGPL - original licence link has changed is not relivant.
7399  *
7400  * Fork - LGPL
7401  * <script type="text/javascript">
7402  */
7403
7404 /**
7405  * @class Roo.Toolbar
7406  * Basic Toolbar class.
7407  * @constructor
7408  * Creates a new Toolbar
7409  * @param {Object} container The config object
7410  */ 
7411 Roo.Toolbar = function(container, buttons, config)
7412 {
7413     /// old consturctor format still supported..
7414     if(container instanceof Array){ // omit the container for later rendering
7415         buttons = container;
7416         config = buttons;
7417         container = null;
7418     }
7419     if (typeof(container) == 'object' && container.xtype) {
7420         config = container;
7421         container = config.container;
7422         buttons = config.buttons || []; // not really - use items!!
7423     }
7424     var xitems = [];
7425     if (config && config.items) {
7426         xitems = config.items;
7427         delete config.items;
7428     }
7429     Roo.apply(this, config);
7430     this.buttons = buttons;
7431     
7432     if(container){
7433         this.render(container);
7434     }
7435     this.xitems = xitems;
7436     Roo.each(xitems, function(b) {
7437         this.add(b);
7438     }, this);
7439     
7440 };
7441
7442 Roo.Toolbar.prototype = {
7443     /**
7444      * @cfg {Array} items
7445      * array of button configs or elements to add (will be converted to a MixedCollection)
7446      */
7447     
7448     /**
7449      * @cfg {String/HTMLElement/Element} container
7450      * The id or element that will contain the toolbar
7451      */
7452     // private
7453     render : function(ct){
7454         this.el = Roo.get(ct);
7455         if(this.cls){
7456             this.el.addClass(this.cls);
7457         }
7458         // using a table allows for vertical alignment
7459         // 100% width is needed by Safari...
7460         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
7461         this.tr = this.el.child("tr", true);
7462         var autoId = 0;
7463         this.items = new Roo.util.MixedCollection(false, function(o){
7464             return o.id || ("item" + (++autoId));
7465         });
7466         if(this.buttons){
7467             this.add.apply(this, this.buttons);
7468             delete this.buttons;
7469         }
7470     },
7471
7472     /**
7473      * Adds element(s) to the toolbar -- this function takes a variable number of 
7474      * arguments of mixed type and adds them to the toolbar.
7475      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
7476      * <ul>
7477      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
7478      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
7479      * <li>Field: Any form field (equivalent to {@link #addField})</li>
7480      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
7481      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
7482      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
7483      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
7484      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
7485      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
7486      * </ul>
7487      * @param {Mixed} arg2
7488      * @param {Mixed} etc.
7489      */
7490     add : function(){
7491         var a = arguments, l = a.length;
7492         for(var i = 0; i < l; i++){
7493             this._add(a[i]);
7494         }
7495     },
7496     // private..
7497     _add : function(el) {
7498         
7499         if (el.xtype) {
7500             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
7501         }
7502         
7503         if (el.applyTo){ // some kind of form field
7504             return this.addField(el);
7505         } 
7506         if (el.render){ // some kind of Toolbar.Item
7507             return this.addItem(el);
7508         }
7509         if (typeof el == "string"){ // string
7510             if(el == "separator" || el == "-"){
7511                 return this.addSeparator();
7512             }
7513             if (el == " "){
7514                 return this.addSpacer();
7515             }
7516             if(el == "->"){
7517                 return this.addFill();
7518             }
7519             return this.addText(el);
7520             
7521         }
7522         if(el.tagName){ // element
7523             return this.addElement(el);
7524         }
7525         if(typeof el == "object"){ // must be button config?
7526             return this.addButton(el);
7527         }
7528         // and now what?!?!
7529         return false;
7530         
7531     },
7532     
7533     /**
7534      * Add an Xtype element
7535      * @param {Object} xtype Xtype Object
7536      * @return {Object} created Object
7537      */
7538     addxtype : function(e){
7539         return this.add(e);  
7540     },
7541     
7542     /**
7543      * Returns the Element for this toolbar.
7544      * @return {Roo.Element}
7545      */
7546     getEl : function(){
7547         return this.el;  
7548     },
7549     
7550     /**
7551      * Adds a separator
7552      * @return {Roo.Toolbar.Item} The separator item
7553      */
7554     addSeparator : function(){
7555         return this.addItem(new Roo.Toolbar.Separator());
7556     },
7557
7558     /**
7559      * Adds a spacer element
7560      * @return {Roo.Toolbar.Spacer} The spacer item
7561      */
7562     addSpacer : function(){
7563         return this.addItem(new Roo.Toolbar.Spacer());
7564     },
7565
7566     /**
7567      * Adds a fill element that forces subsequent additions to the right side of the toolbar
7568      * @return {Roo.Toolbar.Fill} The fill item
7569      */
7570     addFill : function(){
7571         return this.addItem(new Roo.Toolbar.Fill());
7572     },
7573
7574     /**
7575      * Adds any standard HTML element to the toolbar
7576      * @param {String/HTMLElement/Element} el The element or id of the element to add
7577      * @return {Roo.Toolbar.Item} The element's item
7578      */
7579     addElement : function(el){
7580         return this.addItem(new Roo.Toolbar.Item(el));
7581     },
7582     /**
7583      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
7584      * @type Roo.util.MixedCollection  
7585      */
7586     items : false,
7587      
7588     /**
7589      * Adds any Toolbar.Item or subclass
7590      * @param {Roo.Toolbar.Item} item
7591      * @return {Roo.Toolbar.Item} The item
7592      */
7593     addItem : function(item){
7594         var td = this.nextBlock();
7595         item.render(td);
7596         this.items.add(item);
7597         return item;
7598     },
7599     
7600     /**
7601      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
7602      * @param {Object/Array} config A button config or array of configs
7603      * @return {Roo.Toolbar.Button/Array}
7604      */
7605     addButton : function(config){
7606         if(config instanceof Array){
7607             var buttons = [];
7608             for(var i = 0, len = config.length; i < len; i++) {
7609                 buttons.push(this.addButton(config[i]));
7610             }
7611             return buttons;
7612         }
7613         var b = config;
7614         if(!(config instanceof Roo.Toolbar.Button)){
7615             b = config.split ?
7616                 new Roo.Toolbar.SplitButton(config) :
7617                 new Roo.Toolbar.Button(config);
7618         }
7619         var td = this.nextBlock();
7620         b.render(td);
7621         this.items.add(b);
7622         return b;
7623     },
7624     
7625     /**
7626      * Adds text to the toolbar
7627      * @param {String} text The text to add
7628      * @return {Roo.Toolbar.Item} The element's item
7629      */
7630     addText : function(text){
7631         return this.addItem(new Roo.Toolbar.TextItem(text));
7632     },
7633     
7634     /**
7635      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
7636      * @param {Number} index The index where the item is to be inserted
7637      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
7638      * @return {Roo.Toolbar.Button/Item}
7639      */
7640     insertButton : function(index, item){
7641         if(item instanceof Array){
7642             var buttons = [];
7643             for(var i = 0, len = item.length; i < len; i++) {
7644                buttons.push(this.insertButton(index + i, item[i]));
7645             }
7646             return buttons;
7647         }
7648         if (!(item instanceof Roo.Toolbar.Button)){
7649            item = new Roo.Toolbar.Button(item);
7650         }
7651         var td = document.createElement("td");
7652         this.tr.insertBefore(td, this.tr.childNodes[index]);
7653         item.render(td);
7654         this.items.insert(index, item);
7655         return item;
7656     },
7657     
7658     /**
7659      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
7660      * @param {Object} config
7661      * @return {Roo.Toolbar.Item} The element's item
7662      */
7663     addDom : function(config, returnEl){
7664         var td = this.nextBlock();
7665         Roo.DomHelper.overwrite(td, config);
7666         var ti = new Roo.Toolbar.Item(td.firstChild);
7667         ti.render(td);
7668         this.items.add(ti);
7669         return ti;
7670     },
7671
7672     /**
7673      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
7674      * @type Roo.util.MixedCollection  
7675      */
7676     fields : false,
7677     
7678     /**
7679      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
7680      * Note: the field should not have been rendered yet. For a field that has already been
7681      * rendered, use {@link #addElement}.
7682      * @param {Roo.form.Field} field
7683      * @return {Roo.ToolbarItem}
7684      */
7685      
7686       
7687     addField : function(field) {
7688         if (!this.fields) {
7689             var autoId = 0;
7690             this.fields = new Roo.util.MixedCollection(false, function(o){
7691                 return o.id || ("item" + (++autoId));
7692             });
7693
7694         }
7695         
7696         var td = this.nextBlock();
7697         field.render(td);
7698         var ti = new Roo.Toolbar.Item(td.firstChild);
7699         ti.render(td);
7700         this.items.add(ti);
7701         this.fields.add(field);
7702         return ti;
7703     },
7704     /**
7705      * Hide the toolbar
7706      * @method hide
7707      */
7708      
7709       
7710     hide : function()
7711     {
7712         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
7713         this.el.child('div').hide();
7714     },
7715     /**
7716      * Show the toolbar
7717      * @method show
7718      */
7719     show : function()
7720     {
7721         this.el.child('div').show();
7722     },
7723       
7724     // private
7725     nextBlock : function(){
7726         var td = document.createElement("td");
7727         this.tr.appendChild(td);
7728         return td;
7729     },
7730
7731     // private
7732     destroy : function(){
7733         if(this.items){ // rendered?
7734             Roo.destroy.apply(Roo, this.items.items);
7735         }
7736         if(this.fields){ // rendered?
7737             Roo.destroy.apply(Roo, this.fields.items);
7738         }
7739         Roo.Element.uncache(this.el, this.tr);
7740     }
7741 };
7742
7743 /**
7744  * @class Roo.Toolbar.Item
7745  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
7746  * @constructor
7747  * Creates a new Item
7748  * @param {HTMLElement} el 
7749  */
7750 Roo.Toolbar.Item = function(el){
7751     var cfg = {};
7752     if (typeof (el.xtype) != 'undefined') {
7753         cfg = el;
7754         el = cfg.el;
7755     }
7756     
7757     this.el = Roo.getDom(el);
7758     this.id = Roo.id(this.el);
7759     this.hidden = false;
7760     
7761     this.addEvents({
7762          /**
7763              * @event render
7764              * Fires when the button is rendered
7765              * @param {Button} this
7766              */
7767         'render': true
7768     });
7769     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
7770 };
7771 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
7772 //Roo.Toolbar.Item.prototype = {
7773     
7774     /**
7775      * Get this item's HTML Element
7776      * @return {HTMLElement}
7777      */
7778     getEl : function(){
7779        return this.el;  
7780     },
7781
7782     // private
7783     render : function(td){
7784         
7785          this.td = td;
7786         td.appendChild(this.el);
7787         
7788         this.fireEvent('render', this);
7789     },
7790     
7791     /**
7792      * Removes and destroys this item.
7793      */
7794     destroy : function(){
7795         this.td.parentNode.removeChild(this.td);
7796     },
7797     
7798     /**
7799      * Shows this item.
7800      */
7801     show: function(){
7802         this.hidden = false;
7803         this.td.style.display = "";
7804     },
7805     
7806     /**
7807      * Hides this item.
7808      */
7809     hide: function(){
7810         this.hidden = true;
7811         this.td.style.display = "none";
7812     },
7813     
7814     /**
7815      * Convenience function for boolean show/hide.
7816      * @param {Boolean} visible true to show/false to hide
7817      */
7818     setVisible: function(visible){
7819         if(visible) {
7820             this.show();
7821         }else{
7822             this.hide();
7823         }
7824     },
7825     
7826     /**
7827      * Try to focus this item.
7828      */
7829     focus : function(){
7830         Roo.fly(this.el).focus();
7831     },
7832     
7833     /**
7834      * Disables this item.
7835      */
7836     disable : function(){
7837         Roo.fly(this.td).addClass("x-item-disabled");
7838         this.disabled = true;
7839         this.el.disabled = true;
7840     },
7841     
7842     /**
7843      * Enables this item.
7844      */
7845     enable : function(){
7846         Roo.fly(this.td).removeClass("x-item-disabled");
7847         this.disabled = false;
7848         this.el.disabled = false;
7849     }
7850 });
7851
7852
7853 /**
7854  * @class Roo.Toolbar.Separator
7855  * @extends Roo.Toolbar.Item
7856  * A simple toolbar separator class
7857  * @constructor
7858  * Creates a new Separator
7859  */
7860 Roo.Toolbar.Separator = function(cfg){
7861     
7862     var s = document.createElement("span");
7863     s.className = "ytb-sep";
7864     if (cfg) {
7865         cfg.el = s;
7866     }
7867     
7868     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
7869 };
7870 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
7871     enable:Roo.emptyFn,
7872     disable:Roo.emptyFn,
7873     focus:Roo.emptyFn
7874 });
7875
7876 /**
7877  * @class Roo.Toolbar.Spacer
7878  * @extends Roo.Toolbar.Item
7879  * A simple element that adds extra horizontal space to a toolbar.
7880  * @constructor
7881  * Creates a new Spacer
7882  */
7883 Roo.Toolbar.Spacer = function(cfg){
7884     var s = document.createElement("div");
7885     s.className = "ytb-spacer";
7886     if (cfg) {
7887         cfg.el = s;
7888     }
7889     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
7890 };
7891 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
7892     enable:Roo.emptyFn,
7893     disable:Roo.emptyFn,
7894     focus:Roo.emptyFn
7895 });
7896
7897 /**
7898  * @class Roo.Toolbar.Fill
7899  * @extends Roo.Toolbar.Spacer
7900  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
7901  * @constructor
7902  * Creates a new Spacer
7903  */
7904 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
7905     // private
7906     render : function(td){
7907         td.style.width = '100%';
7908         Roo.Toolbar.Fill.superclass.render.call(this, td);
7909     }
7910 });
7911
7912 /**
7913  * @class Roo.Toolbar.TextItem
7914  * @extends Roo.Toolbar.Item
7915  * A simple class that renders text directly into a toolbar.
7916  * @constructor
7917  * Creates a new TextItem
7918  * @param {String} text
7919  */
7920 Roo.Toolbar.TextItem = function(cfg){
7921     var  text = cfg || "";
7922     if (typeof(cfg) == 'object') {
7923         text = cfg.text || "";
7924     }  else {
7925         cfg = null;
7926     }
7927     var s = document.createElement("span");
7928     s.className = "ytb-text";
7929     s.innerHTML = text;
7930     if (cfg) {
7931         cfg.el  = s;
7932     }
7933     
7934     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
7935 };
7936 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
7937     
7938      
7939     enable:Roo.emptyFn,
7940     disable:Roo.emptyFn,
7941     focus:Roo.emptyFn
7942 });
7943
7944 /**
7945  * @class Roo.Toolbar.Button
7946  * @extends Roo.Button
7947  * A button that renders into a toolbar.
7948  * @constructor
7949  * Creates a new Button
7950  * @param {Object} config A standard {@link Roo.Button} config object
7951  */
7952 Roo.Toolbar.Button = function(config){
7953     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
7954 };
7955 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
7956     render : function(td){
7957         this.td = td;
7958         Roo.Toolbar.Button.superclass.render.call(this, td);
7959     },
7960     
7961     /**
7962      * Removes and destroys this button
7963      */
7964     destroy : function(){
7965         Roo.Toolbar.Button.superclass.destroy.call(this);
7966         this.td.parentNode.removeChild(this.td);
7967     },
7968     
7969     /**
7970      * Shows this button
7971      */
7972     show: function(){
7973         this.hidden = false;
7974         this.td.style.display = "";
7975     },
7976     
7977     /**
7978      * Hides this button
7979      */
7980     hide: function(){
7981         this.hidden = true;
7982         this.td.style.display = "none";
7983     },
7984
7985     /**
7986      * Disables this item
7987      */
7988     disable : function(){
7989         Roo.fly(this.td).addClass("x-item-disabled");
7990         this.disabled = true;
7991     },
7992
7993     /**
7994      * Enables this item
7995      */
7996     enable : function(){
7997         Roo.fly(this.td).removeClass("x-item-disabled");
7998         this.disabled = false;
7999     }
8000 });
8001 // backwards compat
8002 Roo.ToolbarButton = Roo.Toolbar.Button;
8003
8004 /**
8005  * @class Roo.Toolbar.SplitButton
8006  * @extends Roo.SplitButton
8007  * A menu button that renders into a toolbar.
8008  * @constructor
8009  * Creates a new SplitButton
8010  * @param {Object} config A standard {@link Roo.SplitButton} config object
8011  */
8012 Roo.Toolbar.SplitButton = function(config){
8013     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
8014 };
8015 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
8016     render : function(td){
8017         this.td = td;
8018         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
8019     },
8020     
8021     /**
8022      * Removes and destroys this button
8023      */
8024     destroy : function(){
8025         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
8026         this.td.parentNode.removeChild(this.td);
8027     },
8028     
8029     /**
8030      * Shows this button
8031      */
8032     show: function(){
8033         this.hidden = false;
8034         this.td.style.display = "";
8035     },
8036     
8037     /**
8038      * Hides this button
8039      */
8040     hide: function(){
8041         this.hidden = true;
8042         this.td.style.display = "none";
8043     }
8044 });
8045
8046 // backwards compat
8047 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
8048  * Based on:
8049  * Ext JS Library 1.1.1
8050  * Copyright(c) 2006-2007, Ext JS, LLC.
8051  *
8052  * Originally Released Under LGPL - original licence link has changed is not relivant.
8053  *
8054  * Fork - LGPL
8055  * <script type="text/javascript">
8056  */
8057  
8058 /**
8059  * @class Roo.PagingToolbar
8060  * @extends Roo.Toolbar
8061  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
8062  * @constructor
8063  * Create a new PagingToolbar
8064  * @param {Object} config The config object
8065  */
8066 Roo.PagingToolbar = function(el, ds, config)
8067 {
8068     // old args format still supported... - xtype is prefered..
8069     if (typeof(el) == 'object' && el.xtype) {
8070         // created from xtype...
8071         config = el;
8072         ds = el.dataSource;
8073         el = config.container;
8074     }
8075     var items = [];
8076     if (config.items) {
8077         items = config.items;
8078         config.items = [];
8079     }
8080     
8081     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
8082     this.ds = ds;
8083     this.cursor = 0;
8084     this.renderButtons(this.el);
8085     this.bind(ds);
8086     
8087     // supprot items array.
8088    
8089     Roo.each(items, function(e) {
8090         this.add(Roo.factory(e));
8091     },this);
8092     
8093 };
8094
8095 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
8096     /**
8097      * @cfg {Roo.data.Store} dataSource
8098      * The underlying data store providing the paged data
8099      */
8100     /**
8101      * @cfg {String/HTMLElement/Element} container
8102      * container The id or element that will contain the toolbar
8103      */
8104     /**
8105      * @cfg {Boolean} displayInfo
8106      * True to display the displayMsg (defaults to false)
8107      */
8108     /**
8109      * @cfg {Number} pageSize
8110      * The number of records to display per page (defaults to 20)
8111      */
8112     pageSize: 20,
8113     /**
8114      * @cfg {String} displayMsg
8115      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
8116      */
8117     displayMsg : 'Displaying {0} - {1} of {2}',
8118     /**
8119      * @cfg {String} emptyMsg
8120      * The message to display when no records are found (defaults to "No data to display")
8121      */
8122     emptyMsg : 'No data to display',
8123     /**
8124      * Customizable piece of the default paging text (defaults to "Page")
8125      * @type String
8126      */
8127     beforePageText : "Page",
8128     /**
8129      * Customizable piece of the default paging text (defaults to "of %0")
8130      * @type String
8131      */
8132     afterPageText : "of {0}",
8133     /**
8134      * Customizable piece of the default paging text (defaults to "First Page")
8135      * @type String
8136      */
8137     firstText : "First Page",
8138     /**
8139      * Customizable piece of the default paging text (defaults to "Previous Page")
8140      * @type String
8141      */
8142     prevText : "Previous Page",
8143     /**
8144      * Customizable piece of the default paging text (defaults to "Next Page")
8145      * @type String
8146      */
8147     nextText : "Next Page",
8148     /**
8149      * Customizable piece of the default paging text (defaults to "Last Page")
8150      * @type String
8151      */
8152     lastText : "Last Page",
8153     /**
8154      * Customizable piece of the default paging text (defaults to "Refresh")
8155      * @type String
8156      */
8157     refreshText : "Refresh",
8158
8159     // private
8160     renderButtons : function(el){
8161         Roo.PagingToolbar.superclass.render.call(this, el);
8162         this.first = this.addButton({
8163             tooltip: this.firstText,
8164             cls: "x-btn-icon x-grid-page-first",
8165             disabled: true,
8166             handler: this.onClick.createDelegate(this, ["first"])
8167         });
8168         this.prev = this.addButton({
8169             tooltip: this.prevText,
8170             cls: "x-btn-icon x-grid-page-prev",
8171             disabled: true,
8172             handler: this.onClick.createDelegate(this, ["prev"])
8173         });
8174         //this.addSeparator();
8175         this.add(this.beforePageText);
8176         this.field = Roo.get(this.addDom({
8177            tag: "input",
8178            type: "text",
8179            size: "3",
8180            value: "1",
8181            cls: "x-grid-page-number"
8182         }).el);
8183         this.field.on("keydown", this.onPagingKeydown, this);
8184         this.field.on("focus", function(){this.dom.select();});
8185         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
8186         this.field.setHeight(18);
8187         //this.addSeparator();
8188         this.next = this.addButton({
8189             tooltip: this.nextText,
8190             cls: "x-btn-icon x-grid-page-next",
8191             disabled: true,
8192             handler: this.onClick.createDelegate(this, ["next"])
8193         });
8194         this.last = this.addButton({
8195             tooltip: this.lastText,
8196             cls: "x-btn-icon x-grid-page-last",
8197             disabled: true,
8198             handler: this.onClick.createDelegate(this, ["last"])
8199         });
8200         //this.addSeparator();
8201         this.loading = this.addButton({
8202             tooltip: this.refreshText,
8203             cls: "x-btn-icon x-grid-loading",
8204             handler: this.onClick.createDelegate(this, ["refresh"])
8205         });
8206
8207         if(this.displayInfo){
8208             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
8209         }
8210     },
8211
8212     // private
8213     updateInfo : function(){
8214         if(this.displayEl){
8215             var count = this.ds.getCount();
8216             var msg = count == 0 ?
8217                 this.emptyMsg :
8218                 String.format(
8219                     this.displayMsg,
8220                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
8221                 );
8222             this.displayEl.update(msg);
8223         }
8224     },
8225
8226     // private
8227     onLoad : function(ds, r, o){
8228        this.cursor = o.params ? o.params.start : 0;
8229        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
8230
8231        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
8232        this.field.dom.value = ap;
8233        this.first.setDisabled(ap == 1);
8234        this.prev.setDisabled(ap == 1);
8235        this.next.setDisabled(ap == ps);
8236        this.last.setDisabled(ap == ps);
8237        this.loading.enable();
8238        this.updateInfo();
8239     },
8240
8241     // private
8242     getPageData : function(){
8243         var total = this.ds.getTotalCount();
8244         return {
8245             total : total,
8246             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
8247             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
8248         };
8249     },
8250
8251     // private
8252     onLoadError : function(){
8253         this.loading.enable();
8254     },
8255
8256     // private
8257     onPagingKeydown : function(e){
8258         var k = e.getKey();
8259         var d = this.getPageData();
8260         if(k == e.RETURN){
8261             var v = this.field.dom.value, pageNum;
8262             if(!v || isNaN(pageNum = parseInt(v, 10))){
8263                 this.field.dom.value = d.activePage;
8264                 return;
8265             }
8266             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
8267             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
8268             e.stopEvent();
8269         }
8270         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))
8271         {
8272           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
8273           this.field.dom.value = pageNum;
8274           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
8275           e.stopEvent();
8276         }
8277         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
8278         {
8279           var v = this.field.dom.value, pageNum; 
8280           var increment = (e.shiftKey) ? 10 : 1;
8281           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
8282             increment *= -1;
8283           }
8284           if(!v || isNaN(pageNum = parseInt(v, 10))) {
8285             this.field.dom.value = d.activePage;
8286             return;
8287           }
8288           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
8289           {
8290             this.field.dom.value = parseInt(v, 10) + increment;
8291             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
8292             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
8293           }
8294           e.stopEvent();
8295         }
8296     },
8297
8298     // private
8299     beforeLoad : function(){
8300         if(this.loading){
8301             this.loading.disable();
8302         }
8303     },
8304
8305     // private
8306     onClick : function(which){
8307         var ds = this.ds;
8308         switch(which){
8309             case "first":
8310                 ds.load({params:{start: 0, limit: this.pageSize}});
8311             break;
8312             case "prev":
8313                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
8314             break;
8315             case "next":
8316                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
8317             break;
8318             case "last":
8319                 var total = ds.getTotalCount();
8320                 var extra = total % this.pageSize;
8321                 var lastStart = extra ? (total - extra) : total-this.pageSize;
8322                 ds.load({params:{start: lastStart, limit: this.pageSize}});
8323             break;
8324             case "refresh":
8325                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
8326             break;
8327         }
8328     },
8329
8330     /**
8331      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
8332      * @param {Roo.data.Store} store The data store to unbind
8333      */
8334     unbind : function(ds){
8335         ds.un("beforeload", this.beforeLoad, this);
8336         ds.un("load", this.onLoad, this);
8337         ds.un("loadexception", this.onLoadError, this);
8338         ds.un("remove", this.updateInfo, this);
8339         ds.un("add", this.updateInfo, this);
8340         this.ds = undefined;
8341     },
8342
8343     /**
8344      * Binds the paging toolbar to the specified {@link Roo.data.Store}
8345      * @param {Roo.data.Store} store The data store to bind
8346      */
8347     bind : function(ds){
8348         ds.on("beforeload", this.beforeLoad, this);
8349         ds.on("load", this.onLoad, this);
8350         ds.on("loadexception", this.onLoadError, this);
8351         ds.on("remove", this.updateInfo, this);
8352         ds.on("add", this.updateInfo, this);
8353         this.ds = ds;
8354     }
8355 });/*
8356  * Based on:
8357  * Ext JS Library 1.1.1
8358  * Copyright(c) 2006-2007, Ext JS, LLC.
8359  *
8360  * Originally Released Under LGPL - original licence link has changed is not relivant.
8361  *
8362  * Fork - LGPL
8363  * <script type="text/javascript">
8364  */
8365
8366 /**
8367  * @class Roo.Resizable
8368  * @extends Roo.util.Observable
8369  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
8370  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
8371  * 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
8372  * the element will be wrapped for you automatically.</p>
8373  * <p>Here is the list of valid resize handles:</p>
8374  * <pre>
8375 Value   Description
8376 ------  -------------------
8377  'n'     north
8378  's'     south
8379  'e'     east
8380  'w'     west
8381  'nw'    northwest
8382  'sw'    southwest
8383  'se'    southeast
8384  'ne'    northeast
8385  'hd'    horizontal drag
8386  'all'   all
8387 </pre>
8388  * <p>Here's an example showing the creation of a typical Resizable:</p>
8389  * <pre><code>
8390 var resizer = new Roo.Resizable("element-id", {
8391     handles: 'all',
8392     minWidth: 200,
8393     minHeight: 100,
8394     maxWidth: 500,
8395     maxHeight: 400,
8396     pinned: true
8397 });
8398 resizer.on("resize", myHandler);
8399 </code></pre>
8400  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
8401  * resizer.east.setDisplayed(false);</p>
8402  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
8403  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
8404  * resize operation's new size (defaults to [0, 0])
8405  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
8406  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
8407  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
8408  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
8409  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
8410  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
8411  * @cfg {Number} width The width of the element in pixels (defaults to null)
8412  * @cfg {Number} height The height of the element in pixels (defaults to null)
8413  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
8414  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
8415  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
8416  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
8417  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
8418  * in favor of the handles config option (defaults to false)
8419  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
8420  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
8421  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
8422  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
8423  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
8424  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
8425  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
8426  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
8427  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
8428  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
8429  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
8430  * @constructor
8431  * Create a new resizable component
8432  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
8433  * @param {Object} config configuration options
8434   */
8435 Roo.Resizable = function(el, config)
8436 {
8437     this.el = Roo.get(el);
8438
8439     if(config && config.wrap){
8440         config.resizeChild = this.el;
8441         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
8442         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
8443         this.el.setStyle("overflow", "hidden");
8444         this.el.setPositioning(config.resizeChild.getPositioning());
8445         config.resizeChild.clearPositioning();
8446         if(!config.width || !config.height){
8447             var csize = config.resizeChild.getSize();
8448             this.el.setSize(csize.width, csize.height);
8449         }
8450         if(config.pinned && !config.adjustments){
8451             config.adjustments = "auto";
8452         }
8453     }
8454
8455     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
8456     this.proxy.unselectable();
8457     this.proxy.enableDisplayMode('block');
8458
8459     Roo.apply(this, config);
8460
8461     if(this.pinned){
8462         this.disableTrackOver = true;
8463         this.el.addClass("x-resizable-pinned");
8464     }
8465     // if the element isn't positioned, make it relative
8466     var position = this.el.getStyle("position");
8467     if(position != "absolute" && position != "fixed"){
8468         this.el.setStyle("position", "relative");
8469     }
8470     if(!this.handles){ // no handles passed, must be legacy style
8471         this.handles = 's,e,se';
8472         if(this.multiDirectional){
8473             this.handles += ',n,w';
8474         }
8475     }
8476     if(this.handles == "all"){
8477         this.handles = "n s e w ne nw se sw";
8478     }
8479     var hs = this.handles.split(/\s*?[,;]\s*?| /);
8480     var ps = Roo.Resizable.positions;
8481     for(var i = 0, len = hs.length; i < len; i++){
8482         if(hs[i] && ps[hs[i]]){
8483             var pos = ps[hs[i]];
8484             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
8485         }
8486     }
8487     // legacy
8488     this.corner = this.southeast;
8489     
8490     // updateBox = the box can move..
8491     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
8492         this.updateBox = true;
8493     }
8494
8495     this.activeHandle = null;
8496
8497     if(this.resizeChild){
8498         if(typeof this.resizeChild == "boolean"){
8499             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
8500         }else{
8501             this.resizeChild = Roo.get(this.resizeChild, true);
8502         }
8503     }
8504     
8505     if(this.adjustments == "auto"){
8506         var rc = this.resizeChild;
8507         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
8508         if(rc && (hw || hn)){
8509             rc.position("relative");
8510             rc.setLeft(hw ? hw.el.getWidth() : 0);
8511             rc.setTop(hn ? hn.el.getHeight() : 0);
8512         }
8513         this.adjustments = [
8514             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
8515             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
8516         ];
8517     }
8518
8519     if(this.draggable){
8520         this.dd = this.dynamic ?
8521             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
8522         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
8523     }
8524
8525     // public events
8526     this.addEvents({
8527         /**
8528          * @event beforeresize
8529          * Fired before resize is allowed. Set enabled to false to cancel resize.
8530          * @param {Roo.Resizable} this
8531          * @param {Roo.EventObject} e The mousedown event
8532          */
8533         "beforeresize" : true,
8534         /**
8535          * @event resizing
8536          * Fired a resizing.
8537          * @param {Roo.Resizable} this
8538          * @param {Number} x The new x position
8539          * @param {Number} y The new y position
8540          * @param {Number} w The new w width
8541          * @param {Number} h The new h hight
8542          * @param {Roo.EventObject} e The mouseup event
8543          */
8544         "resizing" : true,
8545         /**
8546          * @event resize
8547          * Fired after a resize.
8548          * @param {Roo.Resizable} this
8549          * @param {Number} width The new width
8550          * @param {Number} height The new height
8551          * @param {Roo.EventObject} e The mouseup event
8552          */
8553         "resize" : true
8554     });
8555
8556     if(this.width !== null && this.height !== null){
8557         this.resizeTo(this.width, this.height);
8558     }else{
8559         this.updateChildSize();
8560     }
8561     if(Roo.isIE){
8562         this.el.dom.style.zoom = 1;
8563     }
8564     Roo.Resizable.superclass.constructor.call(this);
8565 };
8566
8567 Roo.extend(Roo.Resizable, Roo.util.Observable, {
8568         resizeChild : false,
8569         adjustments : [0, 0],
8570         minWidth : 5,
8571         minHeight : 5,
8572         maxWidth : 10000,
8573         maxHeight : 10000,
8574         enabled : true,
8575         animate : false,
8576         duration : .35,
8577         dynamic : false,
8578         handles : false,
8579         multiDirectional : false,
8580         disableTrackOver : false,
8581         easing : 'easeOutStrong',
8582         widthIncrement : 0,
8583         heightIncrement : 0,
8584         pinned : false,
8585         width : null,
8586         height : null,
8587         preserveRatio : false,
8588         transparent: false,
8589         minX: 0,
8590         minY: 0,
8591         draggable: false,
8592
8593         /**
8594          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
8595          */
8596         constrainTo: undefined,
8597         /**
8598          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
8599          */
8600         resizeRegion: undefined,
8601
8602
8603     /**
8604      * Perform a manual resize
8605      * @param {Number} width
8606      * @param {Number} height
8607      */
8608     resizeTo : function(width, height){
8609         this.el.setSize(width, height);
8610         this.updateChildSize();
8611         this.fireEvent("resize", this, width, height, null);
8612     },
8613
8614     // private
8615     startSizing : function(e, handle){
8616         this.fireEvent("beforeresize", this, e);
8617         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
8618
8619             if(!this.overlay){
8620                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
8621                 this.overlay.unselectable();
8622                 this.overlay.enableDisplayMode("block");
8623                 this.overlay.on("mousemove", this.onMouseMove, this);
8624                 this.overlay.on("mouseup", this.onMouseUp, this);
8625             }
8626             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
8627
8628             this.resizing = true;
8629             this.startBox = this.el.getBox();
8630             this.startPoint = e.getXY();
8631             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
8632                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
8633
8634             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8635             this.overlay.show();
8636
8637             if(this.constrainTo) {
8638                 var ct = Roo.get(this.constrainTo);
8639                 this.resizeRegion = ct.getRegion().adjust(
8640                     ct.getFrameWidth('t'),
8641                     ct.getFrameWidth('l'),
8642                     -ct.getFrameWidth('b'),
8643                     -ct.getFrameWidth('r')
8644                 );
8645             }
8646
8647             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
8648             this.proxy.show();
8649             this.proxy.setBox(this.startBox);
8650             if(!this.dynamic){
8651                 this.proxy.setStyle('visibility', 'visible');
8652             }
8653         }
8654     },
8655
8656     // private
8657     onMouseDown : function(handle, e){
8658         if(this.enabled){
8659             e.stopEvent();
8660             this.activeHandle = handle;
8661             this.startSizing(e, handle);
8662         }
8663     },
8664
8665     // private
8666     onMouseUp : function(e){
8667         var size = this.resizeElement();
8668         this.resizing = false;
8669         this.handleOut();
8670         this.overlay.hide();
8671         this.proxy.hide();
8672         this.fireEvent("resize", this, size.width, size.height, e);
8673     },
8674
8675     // private
8676     updateChildSize : function(){
8677         
8678         if(this.resizeChild){
8679             var el = this.el;
8680             var child = this.resizeChild;
8681             var adj = this.adjustments;
8682             if(el.dom.offsetWidth){
8683                 var b = el.getSize(true);
8684                 child.setSize(b.width+adj[0], b.height+adj[1]);
8685             }
8686             // Second call here for IE
8687             // The first call enables instant resizing and
8688             // the second call corrects scroll bars if they
8689             // exist
8690             if(Roo.isIE){
8691                 setTimeout(function(){
8692                     if(el.dom.offsetWidth){
8693                         var b = el.getSize(true);
8694                         child.setSize(b.width+adj[0], b.height+adj[1]);
8695                     }
8696                 }, 10);
8697             }
8698         }
8699     },
8700
8701     // private
8702     snap : function(value, inc, min){
8703         if(!inc || !value) {
8704             return value;
8705         }
8706         var newValue = value;
8707         var m = value % inc;
8708         if(m > 0){
8709             if(m > (inc/2)){
8710                 newValue = value + (inc-m);
8711             }else{
8712                 newValue = value - m;
8713             }
8714         }
8715         return Math.max(min, newValue);
8716     },
8717
8718     // private
8719     resizeElement : function(){
8720         var box = this.proxy.getBox();
8721         if(this.updateBox){
8722             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
8723         }else{
8724             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
8725         }
8726         this.updateChildSize();
8727         if(!this.dynamic){
8728             this.proxy.hide();
8729         }
8730         return box;
8731     },
8732
8733     // private
8734     constrain : function(v, diff, m, mx){
8735         if(v - diff < m){
8736             diff = v - m;
8737         }else if(v - diff > mx){
8738             diff = mx - v;
8739         }
8740         return diff;
8741     },
8742
8743     // private
8744     onMouseMove : function(e){
8745         
8746         if(this.enabled){
8747             try{// try catch so if something goes wrong the user doesn't get hung
8748
8749             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
8750                 return;
8751             }
8752
8753             //var curXY = this.startPoint;
8754             var curSize = this.curSize || this.startBox;
8755             var x = this.startBox.x, y = this.startBox.y;
8756             var ox = x, oy = y;
8757             var w = curSize.width, h = curSize.height;
8758             var ow = w, oh = h;
8759             var mw = this.minWidth, mh = this.minHeight;
8760             var mxw = this.maxWidth, mxh = this.maxHeight;
8761             var wi = this.widthIncrement;
8762             var hi = this.heightIncrement;
8763
8764             var eventXY = e.getXY();
8765             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
8766             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
8767
8768             var pos = this.activeHandle.position;
8769
8770             switch(pos){
8771                 case "east":
8772                     w += diffX;
8773                     w = Math.min(Math.max(mw, w), mxw);
8774                     break;
8775              
8776                 case "south":
8777                     h += diffY;
8778                     h = Math.min(Math.max(mh, h), mxh);
8779                     break;
8780                 case "southeast":
8781                     w += diffX;
8782                     h += diffY;
8783                     w = Math.min(Math.max(mw, w), mxw);
8784                     h = Math.min(Math.max(mh, h), mxh);
8785                     break;
8786                 case "north":
8787                     diffY = this.constrain(h, diffY, mh, mxh);
8788                     y += diffY;
8789                     h -= diffY;
8790                     break;
8791                 case "hdrag":
8792                     
8793                     if (wi) {
8794                         var adiffX = Math.abs(diffX);
8795                         var sub = (adiffX % wi); // how much 
8796                         if (sub > (wi/2)) { // far enough to snap
8797                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
8798                         } else {
8799                             // remove difference.. 
8800                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
8801                         }
8802                     }
8803                     x += diffX;
8804                     x = Math.max(this.minX, x);
8805                     break;
8806                 case "west":
8807                     diffX = this.constrain(w, diffX, mw, mxw);
8808                     x += diffX;
8809                     w -= diffX;
8810                     break;
8811                 case "northeast":
8812                     w += diffX;
8813                     w = Math.min(Math.max(mw, w), mxw);
8814                     diffY = this.constrain(h, diffY, mh, mxh);
8815                     y += diffY;
8816                     h -= diffY;
8817                     break;
8818                 case "northwest":
8819                     diffX = this.constrain(w, diffX, mw, mxw);
8820                     diffY = this.constrain(h, diffY, mh, mxh);
8821                     y += diffY;
8822                     h -= diffY;
8823                     x += diffX;
8824                     w -= diffX;
8825                     break;
8826                case "southwest":
8827                     diffX = this.constrain(w, diffX, mw, mxw);
8828                     h += diffY;
8829                     h = Math.min(Math.max(mh, h), mxh);
8830                     x += diffX;
8831                     w -= diffX;
8832                     break;
8833             }
8834
8835             var sw = this.snap(w, wi, mw);
8836             var sh = this.snap(h, hi, mh);
8837             if(sw != w || sh != h){
8838                 switch(pos){
8839                     case "northeast":
8840                         y -= sh - h;
8841                     break;
8842                     case "north":
8843                         y -= sh - h;
8844                         break;
8845                     case "southwest":
8846                         x -= sw - w;
8847                     break;
8848                     case "west":
8849                         x -= sw - w;
8850                         break;
8851                     case "northwest":
8852                         x -= sw - w;
8853                         y -= sh - h;
8854                     break;
8855                 }
8856                 w = sw;
8857                 h = sh;
8858             }
8859
8860             if(this.preserveRatio){
8861                 switch(pos){
8862                     case "southeast":
8863                     case "east":
8864                         h = oh * (w/ow);
8865                         h = Math.min(Math.max(mh, h), mxh);
8866                         w = ow * (h/oh);
8867                        break;
8868                     case "south":
8869                         w = ow * (h/oh);
8870                         w = Math.min(Math.max(mw, w), mxw);
8871                         h = oh * (w/ow);
8872                         break;
8873                     case "northeast":
8874                         w = ow * (h/oh);
8875                         w = Math.min(Math.max(mw, w), mxw);
8876                         h = oh * (w/ow);
8877                     break;
8878                     case "north":
8879                         var tw = w;
8880                         w = ow * (h/oh);
8881                         w = Math.min(Math.max(mw, w), mxw);
8882                         h = oh * (w/ow);
8883                         x += (tw - w) / 2;
8884                         break;
8885                     case "southwest":
8886                         h = oh * (w/ow);
8887                         h = Math.min(Math.max(mh, h), mxh);
8888                         var tw = w;
8889                         w = ow * (h/oh);
8890                         x += tw - w;
8891                         break;
8892                     case "west":
8893                         var th = h;
8894                         h = oh * (w/ow);
8895                         h = Math.min(Math.max(mh, h), mxh);
8896                         y += (th - h) / 2;
8897                         var tw = w;
8898                         w = ow * (h/oh);
8899                         x += tw - w;
8900                        break;
8901                     case "northwest":
8902                         var tw = w;
8903                         var th = h;
8904                         h = oh * (w/ow);
8905                         h = Math.min(Math.max(mh, h), mxh);
8906                         w = ow * (h/oh);
8907                         y += th - h;
8908                         x += tw - w;
8909                        break;
8910
8911                 }
8912             }
8913             if (pos == 'hdrag') {
8914                 w = ow;
8915             }
8916             this.proxy.setBounds(x, y, w, h);
8917             if(this.dynamic){
8918                 this.resizeElement();
8919             }
8920             }catch(e){}
8921         }
8922         this.fireEvent("resizing", this, x, y, w, h, e);
8923     },
8924
8925     // private
8926     handleOver : function(){
8927         if(this.enabled){
8928             this.el.addClass("x-resizable-over");
8929         }
8930     },
8931
8932     // private
8933     handleOut : function(){
8934         if(!this.resizing){
8935             this.el.removeClass("x-resizable-over");
8936         }
8937     },
8938
8939     /**
8940      * Returns the element this component is bound to.
8941      * @return {Roo.Element}
8942      */
8943     getEl : function(){
8944         return this.el;
8945     },
8946
8947     /**
8948      * Returns the resizeChild element (or null).
8949      * @return {Roo.Element}
8950      */
8951     getResizeChild : function(){
8952         return this.resizeChild;
8953     },
8954     groupHandler : function()
8955     {
8956         
8957     },
8958     /**
8959      * Destroys this resizable. If the element was wrapped and
8960      * removeEl is not true then the element remains.
8961      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
8962      */
8963     destroy : function(removeEl){
8964         this.proxy.remove();
8965         if(this.overlay){
8966             this.overlay.removeAllListeners();
8967             this.overlay.remove();
8968         }
8969         var ps = Roo.Resizable.positions;
8970         for(var k in ps){
8971             if(typeof ps[k] != "function" && this[ps[k]]){
8972                 var h = this[ps[k]];
8973                 h.el.removeAllListeners();
8974                 h.el.remove();
8975             }
8976         }
8977         if(removeEl){
8978             this.el.update("");
8979             this.el.remove();
8980         }
8981     }
8982 });
8983
8984 // private
8985 // hash to map config positions to true positions
8986 Roo.Resizable.positions = {
8987     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
8988     hd: "hdrag"
8989 };
8990
8991 // private
8992 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
8993     if(!this.tpl){
8994         // only initialize the template if resizable is used
8995         var tpl = Roo.DomHelper.createTemplate(
8996             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
8997         );
8998         tpl.compile();
8999         Roo.Resizable.Handle.prototype.tpl = tpl;
9000     }
9001     this.position = pos;
9002     this.rz = rz;
9003     // show north drag fro topdra
9004     var handlepos = pos == 'hdrag' ? 'north' : pos;
9005     
9006     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
9007     if (pos == 'hdrag') {
9008         this.el.setStyle('cursor', 'pointer');
9009     }
9010     this.el.unselectable();
9011     if(transparent){
9012         this.el.setOpacity(0);
9013     }
9014     this.el.on("mousedown", this.onMouseDown, this);
9015     if(!disableTrackOver){
9016         this.el.on("mouseover", this.onMouseOver, this);
9017         this.el.on("mouseout", this.onMouseOut, this);
9018     }
9019 };
9020
9021 // private
9022 Roo.Resizable.Handle.prototype = {
9023     afterResize : function(rz){
9024         Roo.log('after?');
9025         // do nothing
9026     },
9027     // private
9028     onMouseDown : function(e){
9029         this.rz.onMouseDown(this, e);
9030     },
9031     // private
9032     onMouseOver : function(e){
9033         this.rz.handleOver(this, e);
9034     },
9035     // private
9036     onMouseOut : function(e){
9037         this.rz.handleOut(this, e);
9038     }
9039 };/*
9040  * Based on:
9041  * Ext JS Library 1.1.1
9042  * Copyright(c) 2006-2007, Ext JS, LLC.
9043  *
9044  * Originally Released Under LGPL - original licence link has changed is not relivant.
9045  *
9046  * Fork - LGPL
9047  * <script type="text/javascript">
9048  */
9049
9050 /**
9051  * @class Roo.Editor
9052  * @extends Roo.Component
9053  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
9054  * @constructor
9055  * Create a new Editor
9056  * @param {Roo.form.Field} field The Field object (or descendant)
9057  * @param {Object} config The config object
9058  */
9059 Roo.Editor = function(field, config){
9060     Roo.Editor.superclass.constructor.call(this, config);
9061     this.field = field;
9062     this.addEvents({
9063         /**
9064              * @event beforestartedit
9065              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
9066              * false from the handler of this event.
9067              * @param {Editor} this
9068              * @param {Roo.Element} boundEl The underlying element bound to this editor
9069              * @param {Mixed} value The field value being set
9070              */
9071         "beforestartedit" : true,
9072         /**
9073              * @event startedit
9074              * Fires when this editor is displayed
9075              * @param {Roo.Element} boundEl The underlying element bound to this editor
9076              * @param {Mixed} value The starting field value
9077              */
9078         "startedit" : true,
9079         /**
9080              * @event beforecomplete
9081              * Fires after a change has been made to the field, but before the change is reflected in the underlying
9082              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
9083              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
9084              * event will not fire since no edit actually occurred.
9085              * @param {Editor} this
9086              * @param {Mixed} value The current field value
9087              * @param {Mixed} startValue The original field value
9088              */
9089         "beforecomplete" : true,
9090         /**
9091              * @event complete
9092              * Fires after editing is complete and any changed value has been written to the underlying field.
9093              * @param {Editor} this
9094              * @param {Mixed} value The current field value
9095              * @param {Mixed} startValue The original field value
9096              */
9097         "complete" : true,
9098         /**
9099          * @event specialkey
9100          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
9101          * {@link Roo.EventObject#getKey} to determine which key was pressed.
9102          * @param {Roo.form.Field} this
9103          * @param {Roo.EventObject} e The event object
9104          */
9105         "specialkey" : true
9106     });
9107 };
9108
9109 Roo.extend(Roo.Editor, Roo.Component, {
9110     /**
9111      * @cfg {Boolean/String} autosize
9112      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
9113      * or "height" to adopt the height only (defaults to false)
9114      */
9115     /**
9116      * @cfg {Boolean} revertInvalid
9117      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
9118      * validation fails (defaults to true)
9119      */
9120     /**
9121      * @cfg {Boolean} ignoreNoChange
9122      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
9123      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
9124      * will never be ignored.
9125      */
9126     /**
9127      * @cfg {Boolean} hideEl
9128      * False to keep the bound element visible while the editor is displayed (defaults to true)
9129      */
9130     /**
9131      * @cfg {Mixed} value
9132      * The data value of the underlying field (defaults to "")
9133      */
9134     value : "",
9135     /**
9136      * @cfg {String} alignment
9137      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
9138      */
9139     alignment: "c-c?",
9140     /**
9141      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
9142      * for bottom-right shadow (defaults to "frame")
9143      */
9144     shadow : "frame",
9145     /**
9146      * @cfg {Boolean} constrain True to constrain the editor to the viewport
9147      */
9148     constrain : false,
9149     /**
9150      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
9151      */
9152     completeOnEnter : false,
9153     /**
9154      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
9155      */
9156     cancelOnEsc : false,
9157     /**
9158      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
9159      */
9160     updateEl : false,
9161
9162     // private
9163     onRender : function(ct, position){
9164         this.el = new Roo.Layer({
9165             shadow: this.shadow,
9166             cls: "x-editor",
9167             parentEl : ct,
9168             shim : this.shim,
9169             shadowOffset:4,
9170             id: this.id,
9171             constrain: this.constrain
9172         });
9173         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
9174         if(this.field.msgTarget != 'title'){
9175             this.field.msgTarget = 'qtip';
9176         }
9177         this.field.render(this.el);
9178         if(Roo.isGecko){
9179             this.field.el.dom.setAttribute('autocomplete', 'off');
9180         }
9181         this.field.on("specialkey", this.onSpecialKey, this);
9182         if(this.swallowKeys){
9183             this.field.el.swallowEvent(['keydown','keypress']);
9184         }
9185         this.field.show();
9186         this.field.on("blur", this.onBlur, this);
9187         if(this.field.grow){
9188             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
9189         }
9190     },
9191
9192     onSpecialKey : function(field, e)
9193     {
9194         //Roo.log('editor onSpecialKey');
9195         if(this.completeOnEnter && e.getKey() == e.ENTER){
9196             e.stopEvent();
9197             this.completeEdit();
9198             return;
9199         }
9200         // do not fire special key otherwise it might hide close the editor...
9201         if(e.getKey() == e.ENTER){    
9202             return;
9203         }
9204         if(this.cancelOnEsc && e.getKey() == e.ESC){
9205             this.cancelEdit();
9206             return;
9207         } 
9208         this.fireEvent('specialkey', field, e);
9209     
9210     },
9211
9212     /**
9213      * Starts the editing process and shows the editor.
9214      * @param {String/HTMLElement/Element} el The element to edit
9215      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
9216       * to the innerHTML of el.
9217      */
9218     startEdit : function(el, value){
9219         if(this.editing){
9220             this.completeEdit();
9221         }
9222         this.boundEl = Roo.get(el);
9223         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
9224         if(!this.rendered){
9225             this.render(this.parentEl || document.body);
9226         }
9227         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
9228             return;
9229         }
9230         this.startValue = v;
9231         this.field.setValue(v);
9232         if(this.autoSize){
9233             var sz = this.boundEl.getSize();
9234             switch(this.autoSize){
9235                 case "width":
9236                 this.setSize(sz.width,  "");
9237                 break;
9238                 case "height":
9239                 this.setSize("",  sz.height);
9240                 break;
9241                 default:
9242                 this.setSize(sz.width,  sz.height);
9243             }
9244         }
9245         this.el.alignTo(this.boundEl, this.alignment);
9246         this.editing = true;
9247         if(Roo.QuickTips){
9248             Roo.QuickTips.disable();
9249         }
9250         this.show();
9251     },
9252
9253     /**
9254      * Sets the height and width of this editor.
9255      * @param {Number} width The new width
9256      * @param {Number} height The new height
9257      */
9258     setSize : function(w, h){
9259         this.field.setSize(w, h);
9260         if(this.el){
9261             this.el.sync();
9262         }
9263     },
9264
9265     /**
9266      * Realigns the editor to the bound field based on the current alignment config value.
9267      */
9268     realign : function(){
9269         this.el.alignTo(this.boundEl, this.alignment);
9270     },
9271
9272     /**
9273      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
9274      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
9275      */
9276     completeEdit : function(remainVisible){
9277         if(!this.editing){
9278             return;
9279         }
9280         var v = this.getValue();
9281         if(this.revertInvalid !== false && !this.field.isValid()){
9282             v = this.startValue;
9283             this.cancelEdit(true);
9284         }
9285         if(String(v) === String(this.startValue) && this.ignoreNoChange){
9286             this.editing = false;
9287             this.hide();
9288             return;
9289         }
9290         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
9291             this.editing = false;
9292             if(this.updateEl && this.boundEl){
9293                 this.boundEl.update(v);
9294             }
9295             if(remainVisible !== true){
9296                 this.hide();
9297             }
9298             this.fireEvent("complete", this, v, this.startValue);
9299         }
9300     },
9301
9302     // private
9303     onShow : function(){
9304         this.el.show();
9305         if(this.hideEl !== false){
9306             this.boundEl.hide();
9307         }
9308         this.field.show();
9309         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
9310             this.fixIEFocus = true;
9311             this.deferredFocus.defer(50, this);
9312         }else{
9313             this.field.focus();
9314         }
9315         this.fireEvent("startedit", this.boundEl, this.startValue);
9316     },
9317
9318     deferredFocus : function(){
9319         if(this.editing){
9320             this.field.focus();
9321         }
9322     },
9323
9324     /**
9325      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
9326      * reverted to the original starting value.
9327      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
9328      * cancel (defaults to false)
9329      */
9330     cancelEdit : function(remainVisible){
9331         if(this.editing){
9332             this.setValue(this.startValue);
9333             if(remainVisible !== true){
9334                 this.hide();
9335             }
9336         }
9337     },
9338
9339     // private
9340     onBlur : function(){
9341         if(this.allowBlur !== true && this.editing){
9342             this.completeEdit();
9343         }
9344     },
9345
9346     // private
9347     onHide : function(){
9348         if(this.editing){
9349             this.completeEdit();
9350             return;
9351         }
9352         this.field.blur();
9353         if(this.field.collapse){
9354             this.field.collapse();
9355         }
9356         this.el.hide();
9357         if(this.hideEl !== false){
9358             this.boundEl.show();
9359         }
9360         if(Roo.QuickTips){
9361             Roo.QuickTips.enable();
9362         }
9363     },
9364
9365     /**
9366      * Sets the data value of the editor
9367      * @param {Mixed} value Any valid value supported by the underlying field
9368      */
9369     setValue : function(v){
9370         this.field.setValue(v);
9371     },
9372
9373     /**
9374      * Gets the data value of the editor
9375      * @return {Mixed} The data value
9376      */
9377     getValue : function(){
9378         return this.field.getValue();
9379     }
9380 });/*
9381  * Based on:
9382  * Ext JS Library 1.1.1
9383  * Copyright(c) 2006-2007, Ext JS, LLC.
9384  *
9385  * Originally Released Under LGPL - original licence link has changed is not relivant.
9386  *
9387  * Fork - LGPL
9388  * <script type="text/javascript">
9389  */
9390  
9391 /**
9392  * @class Roo.BasicDialog
9393  * @extends Roo.util.Observable
9394  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
9395  * <pre><code>
9396 var dlg = new Roo.BasicDialog("my-dlg", {
9397     height: 200,
9398     width: 300,
9399     minHeight: 100,
9400     minWidth: 150,
9401     modal: true,
9402     proxyDrag: true,
9403     shadow: true
9404 });
9405 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
9406 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
9407 dlg.addButton('Cancel', dlg.hide, dlg);
9408 dlg.show();
9409 </code></pre>
9410   <b>A Dialog should always be a direct child of the body element.</b>
9411  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
9412  * @cfg {String} title Default text to display in the title bar (defaults to null)
9413  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9414  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9415  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
9416  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
9417  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
9418  * (defaults to null with no animation)
9419  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
9420  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
9421  * property for valid values (defaults to 'all')
9422  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
9423  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
9424  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
9425  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
9426  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
9427  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
9428  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
9429  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
9430  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
9431  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
9432  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
9433  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
9434  * draggable = true (defaults to false)
9435  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
9436  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
9437  * shadow (defaults to false)
9438  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
9439  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
9440  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
9441  * @cfg {Array} buttons Array of buttons
9442  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
9443  * @constructor
9444  * Create a new BasicDialog.
9445  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
9446  * @param {Object} config Configuration options
9447  */
9448 Roo.BasicDialog = function(el, config){
9449     this.el = Roo.get(el);
9450     var dh = Roo.DomHelper;
9451     if(!this.el && config && config.autoCreate){
9452         if(typeof config.autoCreate == "object"){
9453             if(!config.autoCreate.id){
9454                 config.autoCreate.id = el;
9455             }
9456             this.el = dh.append(document.body,
9457                         config.autoCreate, true);
9458         }else{
9459             this.el = dh.append(document.body,
9460                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
9461         }
9462     }
9463     el = this.el;
9464     el.setDisplayed(true);
9465     el.hide = this.hideAction;
9466     this.id = el.id;
9467     el.addClass("x-dlg");
9468
9469     Roo.apply(this, config);
9470
9471     this.proxy = el.createProxy("x-dlg-proxy");
9472     this.proxy.hide = this.hideAction;
9473     this.proxy.setOpacity(.5);
9474     this.proxy.hide();
9475
9476     if(config.width){
9477         el.setWidth(config.width);
9478     }
9479     if(config.height){
9480         el.setHeight(config.height);
9481     }
9482     this.size = el.getSize();
9483     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
9484         this.xy = [config.x,config.y];
9485     }else{
9486         this.xy = el.getCenterXY(true);
9487     }
9488     /** The header element @type Roo.Element */
9489     this.header = el.child("> .x-dlg-hd");
9490     /** The body element @type Roo.Element */
9491     this.body = el.child("> .x-dlg-bd");
9492     /** The footer element @type Roo.Element */
9493     this.footer = el.child("> .x-dlg-ft");
9494
9495     if(!this.header){
9496         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
9497     }
9498     if(!this.body){
9499         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
9500     }
9501
9502     this.header.unselectable();
9503     if(this.title){
9504         this.header.update(this.title);
9505     }
9506     // this element allows the dialog to be focused for keyboard event
9507     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
9508     this.focusEl.swallowEvent("click", true);
9509
9510     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
9511
9512     // wrap the body and footer for special rendering
9513     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
9514     if(this.footer){
9515         this.bwrap.dom.appendChild(this.footer.dom);
9516     }
9517
9518     this.bg = this.el.createChild({
9519         tag: "div", cls:"x-dlg-bg",
9520         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
9521     });
9522     this.centerBg = this.bg.child("div.x-dlg-bg-center");
9523
9524
9525     if(this.autoScroll !== false && !this.autoTabs){
9526         this.body.setStyle("overflow", "auto");
9527     }
9528
9529     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
9530
9531     if(this.closable !== false){
9532         this.el.addClass("x-dlg-closable");
9533         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
9534         this.close.on("click", this.closeClick, this);
9535         this.close.addClassOnOver("x-dlg-close-over");
9536     }
9537     if(this.collapsible !== false){
9538         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
9539         this.collapseBtn.on("click", this.collapseClick, this);
9540         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
9541         this.header.on("dblclick", this.collapseClick, this);
9542     }
9543     if(this.resizable !== false){
9544         this.el.addClass("x-dlg-resizable");
9545         this.resizer = new Roo.Resizable(el, {
9546             minWidth: this.minWidth || 80,
9547             minHeight:this.minHeight || 80,
9548             handles: this.resizeHandles || "all",
9549             pinned: true
9550         });
9551         this.resizer.on("beforeresize", this.beforeResize, this);
9552         this.resizer.on("resize", this.onResize, this);
9553     }
9554     if(this.draggable !== false){
9555         el.addClass("x-dlg-draggable");
9556         if (!this.proxyDrag) {
9557             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
9558         }
9559         else {
9560             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
9561         }
9562         dd.setHandleElId(this.header.id);
9563         dd.endDrag = this.endMove.createDelegate(this);
9564         dd.startDrag = this.startMove.createDelegate(this);
9565         dd.onDrag = this.onDrag.createDelegate(this);
9566         dd.scroll = false;
9567         this.dd = dd;
9568     }
9569     if(this.modal){
9570         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
9571         this.mask.enableDisplayMode("block");
9572         this.mask.hide();
9573         this.el.addClass("x-dlg-modal");
9574     }
9575     if(this.shadow){
9576         this.shadow = new Roo.Shadow({
9577             mode : typeof this.shadow == "string" ? this.shadow : "sides",
9578             offset : this.shadowOffset
9579         });
9580     }else{
9581         this.shadowOffset = 0;
9582     }
9583     if(Roo.useShims && this.shim !== false){
9584         this.shim = this.el.createShim();
9585         this.shim.hide = this.hideAction;
9586         this.shim.hide();
9587     }else{
9588         this.shim = false;
9589     }
9590     if(this.autoTabs){
9591         this.initTabs();
9592     }
9593     if (this.buttons) { 
9594         var bts= this.buttons;
9595         this.buttons = [];
9596         Roo.each(bts, function(b) {
9597             this.addButton(b);
9598         }, this);
9599     }
9600     
9601     
9602     this.addEvents({
9603         /**
9604          * @event keydown
9605          * Fires when a key is pressed
9606          * @param {Roo.BasicDialog} this
9607          * @param {Roo.EventObject} e
9608          */
9609         "keydown" : true,
9610         /**
9611          * @event move
9612          * Fires when this dialog is moved by the user.
9613          * @param {Roo.BasicDialog} this
9614          * @param {Number} x The new page X
9615          * @param {Number} y The new page Y
9616          */
9617         "move" : true,
9618         /**
9619          * @event resize
9620          * Fires when this dialog is resized by the user.
9621          * @param {Roo.BasicDialog} this
9622          * @param {Number} width The new width
9623          * @param {Number} height The new height
9624          */
9625         "resize" : true,
9626         /**
9627          * @event beforehide
9628          * Fires before this dialog is hidden.
9629          * @param {Roo.BasicDialog} this
9630          */
9631         "beforehide" : true,
9632         /**
9633          * @event hide
9634          * Fires when this dialog is hidden.
9635          * @param {Roo.BasicDialog} this
9636          */
9637         "hide" : true,
9638         /**
9639          * @event beforeshow
9640          * Fires before this dialog is shown.
9641          * @param {Roo.BasicDialog} this
9642          */
9643         "beforeshow" : true,
9644         /**
9645          * @event show
9646          * Fires when this dialog is shown.
9647          * @param {Roo.BasicDialog} this
9648          */
9649         "show" : true
9650     });
9651     el.on("keydown", this.onKeyDown, this);
9652     el.on("mousedown", this.toFront, this);
9653     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
9654     this.el.hide();
9655     Roo.DialogManager.register(this);
9656     Roo.BasicDialog.superclass.constructor.call(this);
9657 };
9658
9659 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
9660     shadowOffset: Roo.isIE ? 6 : 5,
9661     minHeight: 80,
9662     minWidth: 200,
9663     minButtonWidth: 75,
9664     defaultButton: null,
9665     buttonAlign: "right",
9666     tabTag: 'div',
9667     firstShow: true,
9668
9669     /**
9670      * Sets the dialog title text
9671      * @param {String} text The title text to display
9672      * @return {Roo.BasicDialog} this
9673      */
9674     setTitle : function(text){
9675         this.header.update(text);
9676         return this;
9677     },
9678
9679     // private
9680     closeClick : function(){
9681         this.hide();
9682     },
9683
9684     // private
9685     collapseClick : function(){
9686         this[this.collapsed ? "expand" : "collapse"]();
9687     },
9688
9689     /**
9690      * Collapses the dialog to its minimized state (only the title bar is visible).
9691      * Equivalent to the user clicking the collapse dialog button.
9692      */
9693     collapse : function(){
9694         if(!this.collapsed){
9695             this.collapsed = true;
9696             this.el.addClass("x-dlg-collapsed");
9697             this.restoreHeight = this.el.getHeight();
9698             this.resizeTo(this.el.getWidth(), this.header.getHeight());
9699         }
9700     },
9701
9702     /**
9703      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
9704      * clicking the expand dialog button.
9705      */
9706     expand : function(){
9707         if(this.collapsed){
9708             this.collapsed = false;
9709             this.el.removeClass("x-dlg-collapsed");
9710             this.resizeTo(this.el.getWidth(), this.restoreHeight);
9711         }
9712     },
9713
9714     /**
9715      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
9716      * @return {Roo.TabPanel} The tabs component
9717      */
9718     initTabs : function(){
9719         var tabs = this.getTabs();
9720         while(tabs.getTab(0)){
9721             tabs.removeTab(0);
9722         }
9723         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
9724             var dom = el.dom;
9725             tabs.addTab(Roo.id(dom), dom.title);
9726             dom.title = "";
9727         });
9728         tabs.activate(0);
9729         return tabs;
9730     },
9731
9732     // private
9733     beforeResize : function(){
9734         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
9735     },
9736
9737     // private
9738     onResize : function(){
9739         this.refreshSize();
9740         this.syncBodyHeight();
9741         this.adjustAssets();
9742         this.focus();
9743         this.fireEvent("resize", this, this.size.width, this.size.height);
9744     },
9745
9746     // private
9747     onKeyDown : function(e){
9748         if(this.isVisible()){
9749             this.fireEvent("keydown", this, e);
9750         }
9751     },
9752
9753     /**
9754      * Resizes the dialog.
9755      * @param {Number} width
9756      * @param {Number} height
9757      * @return {Roo.BasicDialog} this
9758      */
9759     resizeTo : function(width, height){
9760         this.el.setSize(width, height);
9761         this.size = {width: width, height: height};
9762         this.syncBodyHeight();
9763         if(this.fixedcenter){
9764             this.center();
9765         }
9766         if(this.isVisible()){
9767             this.constrainXY();
9768             this.adjustAssets();
9769         }
9770         this.fireEvent("resize", this, width, height);
9771         return this;
9772     },
9773
9774
9775     /**
9776      * Resizes the dialog to fit the specified content size.
9777      * @param {Number} width
9778      * @param {Number} height
9779      * @return {Roo.BasicDialog} this
9780      */
9781     setContentSize : function(w, h){
9782         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
9783         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
9784         //if(!this.el.isBorderBox()){
9785             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
9786             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
9787         //}
9788         if(this.tabs){
9789             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
9790             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
9791         }
9792         this.resizeTo(w, h);
9793         return this;
9794     },
9795
9796     /**
9797      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
9798      * executed in response to a particular key being pressed while the dialog is active.
9799      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
9800      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9801      * @param {Function} fn The function to call
9802      * @param {Object} scope (optional) The scope of the function
9803      * @return {Roo.BasicDialog} this
9804      */
9805     addKeyListener : function(key, fn, scope){
9806         var keyCode, shift, ctrl, alt;
9807         if(typeof key == "object" && !(key instanceof Array)){
9808             keyCode = key["key"];
9809             shift = key["shift"];
9810             ctrl = key["ctrl"];
9811             alt = key["alt"];
9812         }else{
9813             keyCode = key;
9814         }
9815         var handler = function(dlg, e){
9816             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
9817                 var k = e.getKey();
9818                 if(keyCode instanceof Array){
9819                     for(var i = 0, len = keyCode.length; i < len; i++){
9820                         if(keyCode[i] == k){
9821                           fn.call(scope || window, dlg, k, e);
9822                           return;
9823                         }
9824                     }
9825                 }else{
9826                     if(k == keyCode){
9827                         fn.call(scope || window, dlg, k, e);
9828                     }
9829                 }
9830             }
9831         };
9832         this.on("keydown", handler);
9833         return this;
9834     },
9835
9836     /**
9837      * Returns the TabPanel component (creates it if it doesn't exist).
9838      * Note: If you wish to simply check for the existence of tabs without creating them,
9839      * check for a null 'tabs' property.
9840      * @return {Roo.TabPanel} The tabs component
9841      */
9842     getTabs : function(){
9843         if(!this.tabs){
9844             this.el.addClass("x-dlg-auto-tabs");
9845             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
9846             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
9847         }
9848         return this.tabs;
9849     },
9850
9851     /**
9852      * Adds a button to the footer section of the dialog.
9853      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
9854      * object or a valid Roo.DomHelper element config
9855      * @param {Function} handler The function called when the button is clicked
9856      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
9857      * @return {Roo.Button} The new button
9858      */
9859     addButton : function(config, handler, scope){
9860         var dh = Roo.DomHelper;
9861         if(!this.footer){
9862             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
9863         }
9864         if(!this.btnContainer){
9865             var tb = this.footer.createChild({
9866
9867                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
9868                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
9869             }, null, true);
9870             this.btnContainer = tb.firstChild.firstChild.firstChild;
9871         }
9872         var bconfig = {
9873             handler: handler,
9874             scope: scope,
9875             minWidth: this.minButtonWidth,
9876             hideParent:true
9877         };
9878         if(typeof config == "string"){
9879             bconfig.text = config;
9880         }else{
9881             if(config.tag){
9882                 bconfig.dhconfig = config;
9883             }else{
9884                 Roo.apply(bconfig, config);
9885             }
9886         }
9887         var fc = false;
9888         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
9889             bconfig.position = Math.max(0, bconfig.position);
9890             fc = this.btnContainer.childNodes[bconfig.position];
9891         }
9892          
9893         var btn = new Roo.Button(
9894             fc ? 
9895                 this.btnContainer.insertBefore(document.createElement("td"),fc)
9896                 : this.btnContainer.appendChild(document.createElement("td")),
9897             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
9898             bconfig
9899         );
9900         this.syncBodyHeight();
9901         if(!this.buttons){
9902             /**
9903              * Array of all the buttons that have been added to this dialog via addButton
9904              * @type Array
9905              */
9906             this.buttons = [];
9907         }
9908         this.buttons.push(btn);
9909         return btn;
9910     },
9911
9912     /**
9913      * Sets the default button to be focused when the dialog is displayed.
9914      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
9915      * @return {Roo.BasicDialog} this
9916      */
9917     setDefaultButton : function(btn){
9918         this.defaultButton = btn;
9919         return this;
9920     },
9921
9922     // private
9923     getHeaderFooterHeight : function(safe){
9924         var height = 0;
9925         if(this.header){
9926            height += this.header.getHeight();
9927         }
9928         if(this.footer){
9929            var fm = this.footer.getMargins();
9930             height += (this.footer.getHeight()+fm.top+fm.bottom);
9931         }
9932         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
9933         height += this.centerBg.getPadding("tb");
9934         return height;
9935     },
9936
9937     // private
9938     syncBodyHeight : function()
9939     {
9940         var bd = this.body, // the text
9941             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
9942             bw = this.bwrap;
9943         var height = this.size.height - this.getHeaderFooterHeight(false);
9944         bd.setHeight(height-bd.getMargins("tb"));
9945         var hh = this.header.getHeight();
9946         var h = this.size.height-hh;
9947         cb.setHeight(h);
9948         
9949         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
9950         bw.setHeight(h-cb.getPadding("tb"));
9951         
9952         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
9953         bd.setWidth(bw.getWidth(true));
9954         if(this.tabs){
9955             this.tabs.syncHeight();
9956             if(Roo.isIE){
9957                 this.tabs.el.repaint();
9958             }
9959         }
9960     },
9961
9962     /**
9963      * Restores the previous state of the dialog if Roo.state is configured.
9964      * @return {Roo.BasicDialog} this
9965      */
9966     restoreState : function(){
9967         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
9968         if(box && box.width){
9969             this.xy = [box.x, box.y];
9970             this.resizeTo(box.width, box.height);
9971         }
9972         return this;
9973     },
9974
9975     // private
9976     beforeShow : function(){
9977         this.expand();
9978         if(this.fixedcenter){
9979             this.xy = this.el.getCenterXY(true);
9980         }
9981         if(this.modal){
9982             Roo.get(document.body).addClass("x-body-masked");
9983             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
9984             this.mask.show();
9985         }
9986         this.constrainXY();
9987     },
9988
9989     // private
9990     animShow : function(){
9991         var b = Roo.get(this.animateTarget).getBox();
9992         this.proxy.setSize(b.width, b.height);
9993         this.proxy.setLocation(b.x, b.y);
9994         this.proxy.show();
9995         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
9996                     true, .35, this.showEl.createDelegate(this));
9997     },
9998
9999     /**
10000      * Shows the dialog.
10001      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
10002      * @return {Roo.BasicDialog} this
10003      */
10004     show : function(animateTarget){
10005         if (this.fireEvent("beforeshow", this) === false){
10006             return;
10007         }
10008         if(this.syncHeightBeforeShow){
10009             this.syncBodyHeight();
10010         }else if(this.firstShow){
10011             this.firstShow = false;
10012             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
10013         }
10014         this.animateTarget = animateTarget || this.animateTarget;
10015         if(!this.el.isVisible()){
10016             this.beforeShow();
10017             if(this.animateTarget && Roo.get(this.animateTarget)){
10018                 this.animShow();
10019             }else{
10020                 this.showEl();
10021             }
10022         }
10023         return this;
10024     },
10025
10026     // private
10027     showEl : function(){
10028         this.proxy.hide();
10029         this.el.setXY(this.xy);
10030         this.el.show();
10031         this.adjustAssets(true);
10032         this.toFront();
10033         this.focus();
10034         // IE peekaboo bug - fix found by Dave Fenwick
10035         if(Roo.isIE){
10036             this.el.repaint();
10037         }
10038         this.fireEvent("show", this);
10039     },
10040
10041     /**
10042      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
10043      * dialog itself will receive focus.
10044      */
10045     focus : function(){
10046         if(this.defaultButton){
10047             this.defaultButton.focus();
10048         }else{
10049             this.focusEl.focus();
10050         }
10051     },
10052
10053     // private
10054     constrainXY : function(){
10055         if(this.constraintoviewport !== false){
10056             if(!this.viewSize){
10057                 if(this.container){
10058                     var s = this.container.getSize();
10059                     this.viewSize = [s.width, s.height];
10060                 }else{
10061                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
10062                 }
10063             }
10064             var s = Roo.get(this.container||document).getScroll();
10065
10066             var x = this.xy[0], y = this.xy[1];
10067             var w = this.size.width, h = this.size.height;
10068             var vw = this.viewSize[0], vh = this.viewSize[1];
10069             // only move it if it needs it
10070             var moved = false;
10071             // first validate right/bottom
10072             if(x + w > vw+s.left){
10073                 x = vw - w;
10074                 moved = true;
10075             }
10076             if(y + h > vh+s.top){
10077                 y = vh - h;
10078                 moved = true;
10079             }
10080             // then make sure top/left isn't negative
10081             if(x < s.left){
10082                 x = s.left;
10083                 moved = true;
10084             }
10085             if(y < s.top){
10086                 y = s.top;
10087                 moved = true;
10088             }
10089             if(moved){
10090                 // cache xy
10091                 this.xy = [x, y];
10092                 if(this.isVisible()){
10093                     this.el.setLocation(x, y);
10094                     this.adjustAssets();
10095                 }
10096             }
10097         }
10098     },
10099
10100     // private
10101     onDrag : function(){
10102         if(!this.proxyDrag){
10103             this.xy = this.el.getXY();
10104             this.adjustAssets();
10105         }
10106     },
10107
10108     // private
10109     adjustAssets : function(doShow){
10110         var x = this.xy[0], y = this.xy[1];
10111         var w = this.size.width, h = this.size.height;
10112         if(doShow === true){
10113             if(this.shadow){
10114                 this.shadow.show(this.el);
10115             }
10116             if(this.shim){
10117                 this.shim.show();
10118             }
10119         }
10120         if(this.shadow && this.shadow.isVisible()){
10121             this.shadow.show(this.el);
10122         }
10123         if(this.shim && this.shim.isVisible()){
10124             this.shim.setBounds(x, y, w, h);
10125         }
10126     },
10127
10128     // private
10129     adjustViewport : function(w, h){
10130         if(!w || !h){
10131             w = Roo.lib.Dom.getViewWidth();
10132             h = Roo.lib.Dom.getViewHeight();
10133         }
10134         // cache the size
10135         this.viewSize = [w, h];
10136         if(this.modal && this.mask.isVisible()){
10137             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
10138             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
10139         }
10140         if(this.isVisible()){
10141             this.constrainXY();
10142         }
10143     },
10144
10145     /**
10146      * Destroys this dialog and all its supporting elements (including any tabs, shim,
10147      * shadow, proxy, mask, etc.)  Also removes all event listeners.
10148      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
10149      */
10150     destroy : function(removeEl){
10151         if(this.isVisible()){
10152             this.animateTarget = null;
10153             this.hide();
10154         }
10155         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
10156         if(this.tabs){
10157             this.tabs.destroy(removeEl);
10158         }
10159         Roo.destroy(
10160              this.shim,
10161              this.proxy,
10162              this.resizer,
10163              this.close,
10164              this.mask
10165         );
10166         if(this.dd){
10167             this.dd.unreg();
10168         }
10169         if(this.buttons){
10170            for(var i = 0, len = this.buttons.length; i < len; i++){
10171                this.buttons[i].destroy();
10172            }
10173         }
10174         this.el.removeAllListeners();
10175         if(removeEl === true){
10176             this.el.update("");
10177             this.el.remove();
10178         }
10179         Roo.DialogManager.unregister(this);
10180     },
10181
10182     // private
10183     startMove : function(){
10184         if(this.proxyDrag){
10185             this.proxy.show();
10186         }
10187         if(this.constraintoviewport !== false){
10188             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
10189         }
10190     },
10191
10192     // private
10193     endMove : function(){
10194         if(!this.proxyDrag){
10195             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
10196         }else{
10197             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
10198             this.proxy.hide();
10199         }
10200         this.refreshSize();
10201         this.adjustAssets();
10202         this.focus();
10203         this.fireEvent("move", this, this.xy[0], this.xy[1]);
10204     },
10205
10206     /**
10207      * Brings this dialog to the front of any other visible dialogs
10208      * @return {Roo.BasicDialog} this
10209      */
10210     toFront : function(){
10211         Roo.DialogManager.bringToFront(this);
10212         return this;
10213     },
10214
10215     /**
10216      * Sends this dialog to the back (under) of any other visible dialogs
10217      * @return {Roo.BasicDialog} this
10218      */
10219     toBack : function(){
10220         Roo.DialogManager.sendToBack(this);
10221         return this;
10222     },
10223
10224     /**
10225      * Centers this dialog in the viewport
10226      * @return {Roo.BasicDialog} this
10227      */
10228     center : function(){
10229         var xy = this.el.getCenterXY(true);
10230         this.moveTo(xy[0], xy[1]);
10231         return this;
10232     },
10233
10234     /**
10235      * Moves the dialog's top-left corner to the specified point
10236      * @param {Number} x
10237      * @param {Number} y
10238      * @return {Roo.BasicDialog} this
10239      */
10240     moveTo : function(x, y){
10241         this.xy = [x,y];
10242         if(this.isVisible()){
10243             this.el.setXY(this.xy);
10244             this.adjustAssets();
10245         }
10246         return this;
10247     },
10248
10249     /**
10250      * Aligns the dialog to the specified element
10251      * @param {String/HTMLElement/Roo.Element} element The element to align to.
10252      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
10253      * @param {Array} offsets (optional) Offset the positioning by [x, y]
10254      * @return {Roo.BasicDialog} this
10255      */
10256     alignTo : function(element, position, offsets){
10257         this.xy = this.el.getAlignToXY(element, position, offsets);
10258         if(this.isVisible()){
10259             this.el.setXY(this.xy);
10260             this.adjustAssets();
10261         }
10262         return this;
10263     },
10264
10265     /**
10266      * Anchors an element to another element and realigns it when the window is resized.
10267      * @param {String/HTMLElement/Roo.Element} element The element to align to.
10268      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
10269      * @param {Array} offsets (optional) Offset the positioning by [x, y]
10270      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
10271      * is a number, it is used as the buffer delay (defaults to 50ms).
10272      * @return {Roo.BasicDialog} this
10273      */
10274     anchorTo : function(el, alignment, offsets, monitorScroll){
10275         var action = function(){
10276             this.alignTo(el, alignment, offsets);
10277         };
10278         Roo.EventManager.onWindowResize(action, this);
10279         var tm = typeof monitorScroll;
10280         if(tm != 'undefined'){
10281             Roo.EventManager.on(window, 'scroll', action, this,
10282                 {buffer: tm == 'number' ? monitorScroll : 50});
10283         }
10284         action.call(this);
10285         return this;
10286     },
10287
10288     /**
10289      * Returns true if the dialog is visible
10290      * @return {Boolean}
10291      */
10292     isVisible : function(){
10293         return this.el.isVisible();
10294     },
10295
10296     // private
10297     animHide : function(callback){
10298         var b = Roo.get(this.animateTarget).getBox();
10299         this.proxy.show();
10300         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
10301         this.el.hide();
10302         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
10303                     this.hideEl.createDelegate(this, [callback]));
10304     },
10305
10306     /**
10307      * Hides the dialog.
10308      * @param {Function} callback (optional) Function to call when the dialog is hidden
10309      * @return {Roo.BasicDialog} this
10310      */
10311     hide : function(callback){
10312         if (this.fireEvent("beforehide", this) === false){
10313             return;
10314         }
10315         if(this.shadow){
10316             this.shadow.hide();
10317         }
10318         if(this.shim) {
10319           this.shim.hide();
10320         }
10321         // sometimes animateTarget seems to get set.. causing problems...
10322         // this just double checks..
10323         if(this.animateTarget && Roo.get(this.animateTarget)) {
10324            this.animHide(callback);
10325         }else{
10326             this.el.hide();
10327             this.hideEl(callback);
10328         }
10329         return this;
10330     },
10331
10332     // private
10333     hideEl : function(callback){
10334         this.proxy.hide();
10335         if(this.modal){
10336             this.mask.hide();
10337             Roo.get(document.body).removeClass("x-body-masked");
10338         }
10339         this.fireEvent("hide", this);
10340         if(typeof callback == "function"){
10341             callback();
10342         }
10343     },
10344
10345     // private
10346     hideAction : function(){
10347         this.setLeft("-10000px");
10348         this.setTop("-10000px");
10349         this.setStyle("visibility", "hidden");
10350     },
10351
10352     // private
10353     refreshSize : function(){
10354         this.size = this.el.getSize();
10355         this.xy = this.el.getXY();
10356         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
10357     },
10358
10359     // private
10360     // z-index is managed by the DialogManager and may be overwritten at any time
10361     setZIndex : function(index){
10362         if(this.modal){
10363             this.mask.setStyle("z-index", index);
10364         }
10365         if(this.shim){
10366             this.shim.setStyle("z-index", ++index);
10367         }
10368         if(this.shadow){
10369             this.shadow.setZIndex(++index);
10370         }
10371         this.el.setStyle("z-index", ++index);
10372         if(this.proxy){
10373             this.proxy.setStyle("z-index", ++index);
10374         }
10375         if(this.resizer){
10376             this.resizer.proxy.setStyle("z-index", ++index);
10377         }
10378
10379         this.lastZIndex = index;
10380     },
10381
10382     /**
10383      * Returns the element for this dialog
10384      * @return {Roo.Element} The underlying dialog Element
10385      */
10386     getEl : function(){
10387         return this.el;
10388     }
10389 });
10390
10391 /**
10392  * @class Roo.DialogManager
10393  * Provides global access to BasicDialogs that have been created and
10394  * support for z-indexing (layering) multiple open dialogs.
10395  */
10396 Roo.DialogManager = function(){
10397     var list = {};
10398     var accessList = [];
10399     var front = null;
10400
10401     // private
10402     var sortDialogs = function(d1, d2){
10403         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
10404     };
10405
10406     // private
10407     var orderDialogs = function(){
10408         accessList.sort(sortDialogs);
10409         var seed = Roo.DialogManager.zseed;
10410         for(var i = 0, len = accessList.length; i < len; i++){
10411             var dlg = accessList[i];
10412             if(dlg){
10413                 dlg.setZIndex(seed + (i*10));
10414             }
10415         }
10416     };
10417
10418     return {
10419         /**
10420          * The starting z-index for BasicDialogs (defaults to 9000)
10421          * @type Number The z-index value
10422          */
10423         zseed : 9000,
10424
10425         // private
10426         register : function(dlg){
10427             list[dlg.id] = dlg;
10428             accessList.push(dlg);
10429         },
10430
10431         // private
10432         unregister : function(dlg){
10433             delete list[dlg.id];
10434             var i=0;
10435             var len=0;
10436             if(!accessList.indexOf){
10437                 for(  i = 0, len = accessList.length; i < len; i++){
10438                     if(accessList[i] == dlg){
10439                         accessList.splice(i, 1);
10440                         return;
10441                     }
10442                 }
10443             }else{
10444                  i = accessList.indexOf(dlg);
10445                 if(i != -1){
10446                     accessList.splice(i, 1);
10447                 }
10448             }
10449         },
10450
10451         /**
10452          * Gets a registered dialog by id
10453          * @param {String/Object} id The id of the dialog or a dialog
10454          * @return {Roo.BasicDialog} this
10455          */
10456         get : function(id){
10457             return typeof id == "object" ? id : list[id];
10458         },
10459
10460         /**
10461          * Brings the specified dialog to the front
10462          * @param {String/Object} dlg The id of the dialog or a dialog
10463          * @return {Roo.BasicDialog} this
10464          */
10465         bringToFront : function(dlg){
10466             dlg = this.get(dlg);
10467             if(dlg != front){
10468                 front = dlg;
10469                 dlg._lastAccess = new Date().getTime();
10470                 orderDialogs();
10471             }
10472             return dlg;
10473         },
10474
10475         /**
10476          * Sends the specified dialog to the back
10477          * @param {String/Object} dlg The id of the dialog or a dialog
10478          * @return {Roo.BasicDialog} this
10479          */
10480         sendToBack : function(dlg){
10481             dlg = this.get(dlg);
10482             dlg._lastAccess = -(new Date().getTime());
10483             orderDialogs();
10484             return dlg;
10485         },
10486
10487         /**
10488          * Hides all dialogs
10489          */
10490         hideAll : function(){
10491             for(var id in list){
10492                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
10493                     list[id].hide();
10494                 }
10495             }
10496         }
10497     };
10498 }();
10499
10500 /**
10501  * @class Roo.LayoutDialog
10502  * @extends Roo.BasicDialog
10503  * Dialog which provides adjustments for working with a layout in a Dialog.
10504  * Add your necessary layout config options to the dialog's config.<br>
10505  * Example usage (including a nested layout):
10506  * <pre><code>
10507 if(!dialog){
10508     dialog = new Roo.LayoutDialog("download-dlg", {
10509         modal: true,
10510         width:600,
10511         height:450,
10512         shadow:true,
10513         minWidth:500,
10514         minHeight:350,
10515         autoTabs:true,
10516         proxyDrag:true,
10517         // layout config merges with the dialog config
10518         center:{
10519             tabPosition: "top",
10520             alwaysShowTabs: true
10521         }
10522     });
10523     dialog.addKeyListener(27, dialog.hide, dialog);
10524     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
10525     dialog.addButton("Build It!", this.getDownload, this);
10526
10527     // we can even add nested layouts
10528     var innerLayout = new Roo.BorderLayout("dl-inner", {
10529         east: {
10530             initialSize: 200,
10531             autoScroll:true,
10532             split:true
10533         },
10534         center: {
10535             autoScroll:true
10536         }
10537     });
10538     innerLayout.beginUpdate();
10539     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
10540     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
10541     innerLayout.endUpdate(true);
10542
10543     var layout = dialog.getLayout();
10544     layout.beginUpdate();
10545     layout.add("center", new Roo.ContentPanel("standard-panel",
10546                         {title: "Download the Source", fitToFrame:true}));
10547     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
10548                {title: "Build your own roo.js"}));
10549     layout.getRegion("center").showPanel(sp);
10550     layout.endUpdate();
10551 }
10552 </code></pre>
10553     * @constructor
10554     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
10555     * @param {Object} config configuration options
10556   */
10557 Roo.LayoutDialog = function(el, cfg){
10558     
10559     var config=  cfg;
10560     if (typeof(cfg) == 'undefined') {
10561         config = Roo.apply({}, el);
10562         // not sure why we use documentElement here.. - it should always be body.
10563         // IE7 borks horribly if we use documentElement.
10564         // webkit also does not like documentElement - it creates a body element...
10565         el = Roo.get( document.body || document.documentElement ).createChild();
10566         //config.autoCreate = true;
10567     }
10568     
10569     
10570     config.autoTabs = false;
10571     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
10572     this.body.setStyle({overflow:"hidden", position:"relative"});
10573     this.layout = new Roo.BorderLayout(this.body.dom, config);
10574     this.layout.monitorWindowResize = false;
10575     this.el.addClass("x-dlg-auto-layout");
10576     // fix case when center region overwrites center function
10577     this.center = Roo.BasicDialog.prototype.center;
10578     this.on("show", this.layout.layout, this.layout, true);
10579     if (config.items) {
10580         var xitems = config.items;
10581         delete config.items;
10582         Roo.each(xitems, this.addxtype, this);
10583     }
10584     
10585     
10586 };
10587 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
10588     /**
10589      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
10590      * @deprecated
10591      */
10592     endUpdate : function(){
10593         this.layout.endUpdate();
10594     },
10595
10596     /**
10597      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
10598      *  @deprecated
10599      */
10600     beginUpdate : function(){
10601         this.layout.beginUpdate();
10602     },
10603
10604     /**
10605      * Get the BorderLayout for this dialog
10606      * @return {Roo.BorderLayout}
10607      */
10608     getLayout : function(){
10609         return this.layout;
10610     },
10611
10612     showEl : function(){
10613         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
10614         if(Roo.isIE7){
10615             this.layout.layout();
10616         }
10617     },
10618
10619     // private
10620     // Use the syncHeightBeforeShow config option to control this automatically
10621     syncBodyHeight : function(){
10622         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
10623         if(this.layout){this.layout.layout();}
10624     },
10625     
10626       /**
10627      * Add an xtype element (actually adds to the layout.)
10628      * @return {Object} xdata xtype object data.
10629      */
10630     
10631     addxtype : function(c) {
10632         return this.layout.addxtype(c);
10633     }
10634 });/*
10635  * Based on:
10636  * Ext JS Library 1.1.1
10637  * Copyright(c) 2006-2007, Ext JS, LLC.
10638  *
10639  * Originally Released Under LGPL - original licence link has changed is not relivant.
10640  *
10641  * Fork - LGPL
10642  * <script type="text/javascript">
10643  */
10644  
10645 /**
10646  * @class Roo.MessageBox
10647  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
10648  * Example usage:
10649  *<pre><code>
10650 // Basic alert:
10651 Roo.Msg.alert('Status', 'Changes saved successfully.');
10652
10653 // Prompt for user data:
10654 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
10655     if (btn == 'ok'){
10656         // process text value...
10657     }
10658 });
10659
10660 // Show a dialog using config options:
10661 Roo.Msg.show({
10662    title:'Save Changes?',
10663    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
10664    buttons: Roo.Msg.YESNOCANCEL,
10665    fn: processResult,
10666    animEl: 'elId'
10667 });
10668 </code></pre>
10669  * @singleton
10670  */
10671 Roo.MessageBox = function(){
10672     var dlg, opt, mask, waitTimer;
10673     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
10674     var buttons, activeTextEl, bwidth;
10675
10676     // private
10677     var handleButton = function(button){
10678         dlg.hide();
10679         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
10680     };
10681
10682     // private
10683     var handleHide = function(){
10684         if(opt && opt.cls){
10685             dlg.el.removeClass(opt.cls);
10686         }
10687         if(waitTimer){
10688             Roo.TaskMgr.stop(waitTimer);
10689             waitTimer = null;
10690         }
10691     };
10692
10693     // private
10694     var updateButtons = function(b){
10695         var width = 0;
10696         if(!b){
10697             buttons["ok"].hide();
10698             buttons["cancel"].hide();
10699             buttons["yes"].hide();
10700             buttons["no"].hide();
10701             dlg.footer.dom.style.display = 'none';
10702             return width;
10703         }
10704         dlg.footer.dom.style.display = '';
10705         for(var k in buttons){
10706             if(typeof buttons[k] != "function"){
10707                 if(b[k]){
10708                     buttons[k].show();
10709                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
10710                     width += buttons[k].el.getWidth()+15;
10711                 }else{
10712                     buttons[k].hide();
10713                 }
10714             }
10715         }
10716         return width;
10717     };
10718
10719     // private
10720     var handleEsc = function(d, k, e){
10721         if(opt && opt.closable !== false){
10722             dlg.hide();
10723         }
10724         if(e){
10725             e.stopEvent();
10726         }
10727     };
10728
10729     return {
10730         /**
10731          * Returns a reference to the underlying {@link Roo.BasicDialog} element
10732          * @return {Roo.BasicDialog} The BasicDialog element
10733          */
10734         getDialog : function(){
10735            if(!dlg){
10736                 dlg = new Roo.BasicDialog("x-msg-box", {
10737                     autoCreate : true,
10738                     shadow: true,
10739                     draggable: true,
10740                     resizable:false,
10741                     constraintoviewport:false,
10742                     fixedcenter:true,
10743                     collapsible : false,
10744                     shim:true,
10745                     modal: true,
10746                     width:400, height:100,
10747                     buttonAlign:"center",
10748                     closeClick : function(){
10749                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
10750                             handleButton("no");
10751                         }else{
10752                             handleButton("cancel");
10753                         }
10754                     }
10755                 });
10756                 dlg.on("hide", handleHide);
10757                 mask = dlg.mask;
10758                 dlg.addKeyListener(27, handleEsc);
10759                 buttons = {};
10760                 var bt = this.buttonText;
10761                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
10762                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
10763                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
10764                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
10765                 bodyEl = dlg.body.createChild({
10766
10767                     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>'
10768                 });
10769                 msgEl = bodyEl.dom.firstChild;
10770                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
10771                 textboxEl.enableDisplayMode();
10772                 textboxEl.addKeyListener([10,13], function(){
10773                     if(dlg.isVisible() && opt && opt.buttons){
10774                         if(opt.buttons.ok){
10775                             handleButton("ok");
10776                         }else if(opt.buttons.yes){
10777                             handleButton("yes");
10778                         }
10779                     }
10780                 });
10781                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
10782                 textareaEl.enableDisplayMode();
10783                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
10784                 progressEl.enableDisplayMode();
10785                 var pf = progressEl.dom.firstChild;
10786                 if (pf) {
10787                     pp = Roo.get(pf.firstChild);
10788                     pp.setHeight(pf.offsetHeight);
10789                 }
10790                 
10791             }
10792             return dlg;
10793         },
10794
10795         /**
10796          * Updates the message box body text
10797          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
10798          * the XHTML-compliant non-breaking space character '&amp;#160;')
10799          * @return {Roo.MessageBox} This message box
10800          */
10801         updateText : function(text){
10802             if(!dlg.isVisible() && !opt.width){
10803                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
10804             }
10805             msgEl.innerHTML = text || '&#160;';
10806       
10807             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
10808             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
10809             var w = Math.max(
10810                     Math.min(opt.width || cw , this.maxWidth), 
10811                     Math.max(opt.minWidth || this.minWidth, bwidth)
10812             );
10813             if(opt.prompt){
10814                 activeTextEl.setWidth(w);
10815             }
10816             if(dlg.isVisible()){
10817                 dlg.fixedcenter = false;
10818             }
10819             // to big, make it scroll. = But as usual stupid IE does not support
10820             // !important..
10821             
10822             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
10823                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
10824                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
10825             } else {
10826                 bodyEl.dom.style.height = '';
10827                 bodyEl.dom.style.overflowY = '';
10828             }
10829             if (cw > w) {
10830                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
10831             } else {
10832                 bodyEl.dom.style.overflowX = '';
10833             }
10834             
10835             dlg.setContentSize(w, bodyEl.getHeight());
10836             if(dlg.isVisible()){
10837                 dlg.fixedcenter = true;
10838             }
10839             return this;
10840         },
10841
10842         /**
10843          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
10844          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
10845          * @param {Number} value Any number between 0 and 1 (e.g., .5)
10846          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
10847          * @return {Roo.MessageBox} This message box
10848          */
10849         updateProgress : function(value, text){
10850             if(text){
10851                 this.updateText(text);
10852             }
10853             if (pp) { // weird bug on my firefox - for some reason this is not defined
10854                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
10855             }
10856             return this;
10857         },        
10858
10859         /**
10860          * Returns true if the message box is currently displayed
10861          * @return {Boolean} True if the message box is visible, else false
10862          */
10863         isVisible : function(){
10864             return dlg && dlg.isVisible();  
10865         },
10866
10867         /**
10868          * Hides the message box if it is displayed
10869          */
10870         hide : function(){
10871             if(this.isVisible()){
10872                 dlg.hide();
10873             }  
10874         },
10875
10876         /**
10877          * Displays a new message box, or reinitializes an existing message box, based on the config options
10878          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
10879          * The following config object properties are supported:
10880          * <pre>
10881 Property    Type             Description
10882 ----------  ---------------  ------------------------------------------------------------------------------------
10883 animEl            String/Element   An id or Element from which the message box should animate as it opens and
10884                                    closes (defaults to undefined)
10885 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
10886                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
10887 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
10888                                    progress and wait dialogs will ignore this property and always hide the
10889                                    close button as they can only be closed programmatically.
10890 cls               String           A custom CSS class to apply to the message box element
10891 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
10892                                    displayed (defaults to 75)
10893 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
10894                                    function will be btn (the name of the button that was clicked, if applicable,
10895                                    e.g. "ok"), and text (the value of the active text field, if applicable).
10896                                    Progress and wait dialogs will ignore this option since they do not respond to
10897                                    user actions and can only be closed programmatically, so any required function
10898                                    should be called by the same code after it closes the dialog.
10899 icon              String           A CSS class that provides a background image to be used as an icon for
10900                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
10901 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
10902 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
10903 modal             Boolean          False to allow user interaction with the page while the message box is
10904                                    displayed (defaults to true)
10905 msg               String           A string that will replace the existing message box body text (defaults
10906                                    to the XHTML-compliant non-breaking space character '&#160;')
10907 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
10908 progress          Boolean          True to display a progress bar (defaults to false)
10909 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
10910 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
10911 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
10912 title             String           The title text
10913 value             String           The string value to set into the active textbox element if displayed
10914 wait              Boolean          True to display a progress bar (defaults to false)
10915 width             Number           The width of the dialog in pixels
10916 </pre>
10917          *
10918          * Example usage:
10919          * <pre><code>
10920 Roo.Msg.show({
10921    title: 'Address',
10922    msg: 'Please enter your address:',
10923    width: 300,
10924    buttons: Roo.MessageBox.OKCANCEL,
10925    multiline: true,
10926    fn: saveAddress,
10927    animEl: 'addAddressBtn'
10928 });
10929 </code></pre>
10930          * @param {Object} config Configuration options
10931          * @return {Roo.MessageBox} This message box
10932          */
10933         show : function(options)
10934         {
10935             
10936             // this causes nightmares if you show one dialog after another
10937             // especially on callbacks..
10938              
10939             if(this.isVisible()){
10940                 
10941                 this.hide();
10942                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
10943                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
10944                 Roo.log("New Dialog Message:" +  options.msg )
10945                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
10946                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
10947                 
10948             }
10949             var d = this.getDialog();
10950             opt = options;
10951             d.setTitle(opt.title || "&#160;");
10952             d.close.setDisplayed(opt.closable !== false);
10953             activeTextEl = textboxEl;
10954             opt.prompt = opt.prompt || (opt.multiline ? true : false);
10955             if(opt.prompt){
10956                 if(opt.multiline){
10957                     textboxEl.hide();
10958                     textareaEl.show();
10959                     textareaEl.setHeight(typeof opt.multiline == "number" ?
10960                         opt.multiline : this.defaultTextHeight);
10961                     activeTextEl = textareaEl;
10962                 }else{
10963                     textboxEl.show();
10964                     textareaEl.hide();
10965                 }
10966             }else{
10967                 textboxEl.hide();
10968                 textareaEl.hide();
10969             }
10970             progressEl.setDisplayed(opt.progress === true);
10971             this.updateProgress(0);
10972             activeTextEl.dom.value = opt.value || "";
10973             if(opt.prompt){
10974                 dlg.setDefaultButton(activeTextEl);
10975             }else{
10976                 var bs = opt.buttons;
10977                 var db = null;
10978                 if(bs && bs.ok){
10979                     db = buttons["ok"];
10980                 }else if(bs && bs.yes){
10981                     db = buttons["yes"];
10982                 }
10983                 dlg.setDefaultButton(db);
10984             }
10985             bwidth = updateButtons(opt.buttons);
10986             this.updateText(opt.msg);
10987             if(opt.cls){
10988                 d.el.addClass(opt.cls);
10989             }
10990             d.proxyDrag = opt.proxyDrag === true;
10991             d.modal = opt.modal !== false;
10992             d.mask = opt.modal !== false ? mask : false;
10993             if(!d.isVisible()){
10994                 // force it to the end of the z-index stack so it gets a cursor in FF
10995                 document.body.appendChild(dlg.el.dom);
10996                 d.animateTarget = null;
10997                 d.show(options.animEl);
10998             }
10999             return this;
11000         },
11001
11002         /**
11003          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
11004          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
11005          * and closing the message box when the process is complete.
11006          * @param {String} title The title bar text
11007          * @param {String} msg The message box body text
11008          * @return {Roo.MessageBox} This message box
11009          */
11010         progress : function(title, msg){
11011             this.show({
11012                 title : title,
11013                 msg : msg,
11014                 buttons: false,
11015                 progress:true,
11016                 closable:false,
11017                 minWidth: this.minProgressWidth,
11018                 modal : true
11019             });
11020             return this;
11021         },
11022
11023         /**
11024          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
11025          * If a callback function is passed it will be called after the user clicks the button, and the
11026          * id of the button that was clicked will be passed as the only parameter to the callback
11027          * (could also be the top-right close button).
11028          * @param {String} title The title bar text
11029          * @param {String} msg The message box body text
11030          * @param {Function} fn (optional) The callback function invoked after the message box is closed
11031          * @param {Object} scope (optional) The scope of the callback function
11032          * @return {Roo.MessageBox} This message box
11033          */
11034         alert : function(title, msg, fn, scope){
11035             this.show({
11036                 title : title,
11037                 msg : msg,
11038                 buttons: this.OK,
11039                 fn: fn,
11040                 scope : scope,
11041                 modal : true
11042             });
11043             return this;
11044         },
11045
11046         /**
11047          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
11048          * interaction while waiting for a long-running process to complete that does not have defined intervals.
11049          * You are responsible for closing the message box when the process is complete.
11050          * @param {String} msg The message box body text
11051          * @param {String} title (optional) The title bar text
11052          * @return {Roo.MessageBox} This message box
11053          */
11054         wait : function(msg, title){
11055             this.show({
11056                 title : title,
11057                 msg : msg,
11058                 buttons: false,
11059                 closable:false,
11060                 progress:true,
11061                 modal:true,
11062                 width:300,
11063                 wait:true
11064             });
11065             waitTimer = Roo.TaskMgr.start({
11066                 run: function(i){
11067                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
11068                 },
11069                 interval: 1000
11070             });
11071             return this;
11072         },
11073
11074         /**
11075          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
11076          * If a callback function is passed it will be called after the user clicks either button, and the id of the
11077          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
11078          * @param {String} title The title bar text
11079          * @param {String} msg The message box body text
11080          * @param {Function} fn (optional) The callback function invoked after the message box is closed
11081          * @param {Object} scope (optional) The scope of the callback function
11082          * @return {Roo.MessageBox} This message box
11083          */
11084         confirm : function(title, msg, fn, scope){
11085             this.show({
11086                 title : title,
11087                 msg : msg,
11088                 buttons: this.YESNO,
11089                 fn: fn,
11090                 scope : scope,
11091                 modal : true
11092             });
11093             return this;
11094         },
11095
11096         /**
11097          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
11098          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
11099          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
11100          * (could also be the top-right close button) and the text that was entered will be passed as the two
11101          * parameters to the callback.
11102          * @param {String} title The title bar text
11103          * @param {String} msg The message box body text
11104          * @param {Function} fn (optional) The callback function invoked after the message box is closed
11105          * @param {Object} scope (optional) The scope of the callback function
11106          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
11107          * property, or the height in pixels to create the textbox (defaults to false / single-line)
11108          * @return {Roo.MessageBox} This message box
11109          */
11110         prompt : function(title, msg, fn, scope, multiline){
11111             this.show({
11112                 title : title,
11113                 msg : msg,
11114                 buttons: this.OKCANCEL,
11115                 fn: fn,
11116                 minWidth:250,
11117                 scope : scope,
11118                 prompt:true,
11119                 multiline: multiline,
11120                 modal : true
11121             });
11122             return this;
11123         },
11124
11125         /**
11126          * Button config that displays a single OK button
11127          * @type Object
11128          */
11129         OK : {ok:true},
11130         /**
11131          * Button config that displays Yes and No buttons
11132          * @type Object
11133          */
11134         YESNO : {yes:true, no:true},
11135         /**
11136          * Button config that displays OK and Cancel buttons
11137          * @type Object
11138          */
11139         OKCANCEL : {ok:true, cancel:true},
11140         /**
11141          * Button config that displays Yes, No and Cancel buttons
11142          * @type Object
11143          */
11144         YESNOCANCEL : {yes:true, no:true, cancel:true},
11145
11146         /**
11147          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
11148          * @type Number
11149          */
11150         defaultTextHeight : 75,
11151         /**
11152          * The maximum width in pixels of the message box (defaults to 600)
11153          * @type Number
11154          */
11155         maxWidth : 600,
11156         /**
11157          * The minimum width in pixels of the message box (defaults to 100)
11158          * @type Number
11159          */
11160         minWidth : 100,
11161         /**
11162          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
11163          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
11164          * @type Number
11165          */
11166         minProgressWidth : 250,
11167         /**
11168          * An object containing the default button text strings that can be overriden for localized language support.
11169          * Supported properties are: ok, cancel, yes and no.
11170          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
11171          * @type Object
11172          */
11173         buttonText : {
11174             ok : "OK",
11175             cancel : "Cancel",
11176             yes : "Yes",
11177             no : "No"
11178         }
11179     };
11180 }();
11181
11182 /**
11183  * Shorthand for {@link Roo.MessageBox}
11184  */
11185 Roo.Msg = Roo.MessageBox;/*
11186  * Based on:
11187  * Ext JS Library 1.1.1
11188  * Copyright(c) 2006-2007, Ext JS, LLC.
11189  *
11190  * Originally Released Under LGPL - original licence link has changed is not relivant.
11191  *
11192  * Fork - LGPL
11193  * <script type="text/javascript">
11194  */
11195 /**
11196  * @class Roo.QuickTips
11197  * Provides attractive and customizable tooltips for any element.
11198  * @singleton
11199  */
11200 Roo.QuickTips = function(){
11201     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
11202     var ce, bd, xy, dd;
11203     var visible = false, disabled = true, inited = false;
11204     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
11205     
11206     var onOver = function(e){
11207         if(disabled){
11208             return;
11209         }
11210         var t = e.getTarget();
11211         if(!t || t.nodeType !== 1 || t == document || t == document.body){
11212             return;
11213         }
11214         if(ce && t == ce.el){
11215             clearTimeout(hideProc);
11216             return;
11217         }
11218         if(t && tagEls[t.id]){
11219             tagEls[t.id].el = t;
11220             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
11221             return;
11222         }
11223         var ttp, et = Roo.fly(t);
11224         var ns = cfg.namespace;
11225         if(tm.interceptTitles && t.title){
11226             ttp = t.title;
11227             t.qtip = ttp;
11228             t.removeAttribute("title");
11229             e.preventDefault();
11230         }else{
11231             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
11232         }
11233         if(ttp){
11234             showProc = show.defer(tm.showDelay, tm, [{
11235                 el: t, 
11236                 text: ttp.replace(/\\n/g,'<br/>'),
11237                 width: et.getAttributeNS(ns, cfg.width),
11238                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
11239                 title: et.getAttributeNS(ns, cfg.title),
11240                     cls: et.getAttributeNS(ns, cfg.cls)
11241             }]);
11242         }
11243     };
11244     
11245     var onOut = function(e){
11246         clearTimeout(showProc);
11247         var t = e.getTarget();
11248         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
11249             hideProc = setTimeout(hide, tm.hideDelay);
11250         }
11251     };
11252     
11253     var onMove = function(e){
11254         if(disabled){
11255             return;
11256         }
11257         xy = e.getXY();
11258         xy[1] += 18;
11259         if(tm.trackMouse && ce){
11260             el.setXY(xy);
11261         }
11262     };
11263     
11264     var onDown = function(e){
11265         clearTimeout(showProc);
11266         clearTimeout(hideProc);
11267         if(!e.within(el)){
11268             if(tm.hideOnClick){
11269                 hide();
11270                 tm.disable();
11271                 tm.enable.defer(100, tm);
11272             }
11273         }
11274     };
11275     
11276     var getPad = function(){
11277         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
11278     };
11279
11280     var show = function(o){
11281         if(disabled){
11282             return;
11283         }
11284         clearTimeout(dismissProc);
11285         ce = o;
11286         if(removeCls){ // in case manually hidden
11287             el.removeClass(removeCls);
11288             removeCls = null;
11289         }
11290         if(ce.cls){
11291             el.addClass(ce.cls);
11292             removeCls = ce.cls;
11293         }
11294         if(ce.title){
11295             tipTitle.update(ce.title);
11296             tipTitle.show();
11297         }else{
11298             tipTitle.update('');
11299             tipTitle.hide();
11300         }
11301         el.dom.style.width  = tm.maxWidth+'px';
11302         //tipBody.dom.style.width = '';
11303         tipBodyText.update(o.text);
11304         var p = getPad(), w = ce.width;
11305         if(!w){
11306             var td = tipBodyText.dom;
11307             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
11308             if(aw > tm.maxWidth){
11309                 w = tm.maxWidth;
11310             }else if(aw < tm.minWidth){
11311                 w = tm.minWidth;
11312             }else{
11313                 w = aw;
11314             }
11315         }
11316         //tipBody.setWidth(w);
11317         el.setWidth(parseInt(w, 10) + p);
11318         if(ce.autoHide === false){
11319             close.setDisplayed(true);
11320             if(dd){
11321                 dd.unlock();
11322             }
11323         }else{
11324             close.setDisplayed(false);
11325             if(dd){
11326                 dd.lock();
11327             }
11328         }
11329         if(xy){
11330             el.avoidY = xy[1]-18;
11331             el.setXY(xy);
11332         }
11333         if(tm.animate){
11334             el.setOpacity(.1);
11335             el.setStyle("visibility", "visible");
11336             el.fadeIn({callback: afterShow});
11337         }else{
11338             afterShow();
11339         }
11340     };
11341     
11342     var afterShow = function(){
11343         if(ce){
11344             el.show();
11345             esc.enable();
11346             if(tm.autoDismiss && ce.autoHide !== false){
11347                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
11348             }
11349         }
11350     };
11351     
11352     var hide = function(noanim){
11353         clearTimeout(dismissProc);
11354         clearTimeout(hideProc);
11355         ce = null;
11356         if(el.isVisible()){
11357             esc.disable();
11358             if(noanim !== true && tm.animate){
11359                 el.fadeOut({callback: afterHide});
11360             }else{
11361                 afterHide();
11362             } 
11363         }
11364     };
11365     
11366     var afterHide = function(){
11367         el.hide();
11368         if(removeCls){
11369             el.removeClass(removeCls);
11370             removeCls = null;
11371         }
11372     };
11373     
11374     return {
11375         /**
11376         * @cfg {Number} minWidth
11377         * The minimum width of the quick tip (defaults to 40)
11378         */
11379        minWidth : 40,
11380         /**
11381         * @cfg {Number} maxWidth
11382         * The maximum width of the quick tip (defaults to 300)
11383         */
11384        maxWidth : 300,
11385         /**
11386         * @cfg {Boolean} interceptTitles
11387         * True to automatically use the element's DOM title value if available (defaults to false)
11388         */
11389        interceptTitles : false,
11390         /**
11391         * @cfg {Boolean} trackMouse
11392         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
11393         */
11394        trackMouse : false,
11395         /**
11396         * @cfg {Boolean} hideOnClick
11397         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
11398         */
11399        hideOnClick : true,
11400         /**
11401         * @cfg {Number} showDelay
11402         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
11403         */
11404        showDelay : 500,
11405         /**
11406         * @cfg {Number} hideDelay
11407         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
11408         */
11409        hideDelay : 200,
11410         /**
11411         * @cfg {Boolean} autoHide
11412         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
11413         * Used in conjunction with hideDelay.
11414         */
11415        autoHide : true,
11416         /**
11417         * @cfg {Boolean}
11418         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
11419         * (defaults to true).  Used in conjunction with autoDismissDelay.
11420         */
11421        autoDismiss : true,
11422         /**
11423         * @cfg {Number}
11424         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
11425         */
11426        autoDismissDelay : 5000,
11427        /**
11428         * @cfg {Boolean} animate
11429         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
11430         */
11431        animate : false,
11432
11433        /**
11434         * @cfg {String} title
11435         * Title text to display (defaults to '').  This can be any valid HTML markup.
11436         */
11437         title: '',
11438        /**
11439         * @cfg {String} text
11440         * Body text to display (defaults to '').  This can be any valid HTML markup.
11441         */
11442         text : '',
11443        /**
11444         * @cfg {String} cls
11445         * A CSS class to apply to the base quick tip element (defaults to '').
11446         */
11447         cls : '',
11448        /**
11449         * @cfg {Number} width
11450         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
11451         * minWidth or maxWidth.
11452         */
11453         width : null,
11454
11455     /**
11456      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
11457      * or display QuickTips in a page.
11458      */
11459        init : function(){
11460           tm = Roo.QuickTips;
11461           cfg = tm.tagConfig;
11462           if(!inited){
11463               if(!Roo.isReady){ // allow calling of init() before onReady
11464                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
11465                   return;
11466               }
11467               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
11468               el.fxDefaults = {stopFx: true};
11469               // maximum custom styling
11470               //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>');
11471               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>');              
11472               tipTitle = el.child('h3');
11473               tipTitle.enableDisplayMode("block");
11474               tipBody = el.child('div.x-tip-bd');
11475               tipBodyText = el.child('div.x-tip-bd-inner');
11476               //bdLeft = el.child('div.x-tip-bd-left');
11477               //bdRight = el.child('div.x-tip-bd-right');
11478               close = el.child('div.x-tip-close');
11479               close.enableDisplayMode("block");
11480               close.on("click", hide);
11481               var d = Roo.get(document);
11482               d.on("mousedown", onDown);
11483               d.on("mouseover", onOver);
11484               d.on("mouseout", onOut);
11485               d.on("mousemove", onMove);
11486               esc = d.addKeyListener(27, hide);
11487               esc.disable();
11488               if(Roo.dd.DD){
11489                   dd = el.initDD("default", null, {
11490                       onDrag : function(){
11491                           el.sync();  
11492                       }
11493                   });
11494                   dd.setHandleElId(tipTitle.id);
11495                   dd.lock();
11496               }
11497               inited = true;
11498           }
11499           this.enable(); 
11500        },
11501
11502     /**
11503      * Configures a new quick tip instance and assigns it to a target element.  The following config options
11504      * are supported:
11505      * <pre>
11506 Property    Type                   Description
11507 ----------  ---------------------  ------------------------------------------------------------------------
11508 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
11509      * </ul>
11510      * @param {Object} config The config object
11511      */
11512        register : function(config){
11513            var cs = config instanceof Array ? config : arguments;
11514            for(var i = 0, len = cs.length; i < len; i++) {
11515                var c = cs[i];
11516                var target = c.target;
11517                if(target){
11518                    if(target instanceof Array){
11519                        for(var j = 0, jlen = target.length; j < jlen; j++){
11520                            tagEls[target[j]] = c;
11521                        }
11522                    }else{
11523                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
11524                    }
11525                }
11526            }
11527        },
11528
11529     /**
11530      * Removes this quick tip from its element and destroys it.
11531      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
11532      */
11533        unregister : function(el){
11534            delete tagEls[Roo.id(el)];
11535        },
11536
11537     /**
11538      * Enable this quick tip.
11539      */
11540        enable : function(){
11541            if(inited && disabled){
11542                locks.pop();
11543                if(locks.length < 1){
11544                    disabled = false;
11545                }
11546            }
11547        },
11548
11549     /**
11550      * Disable this quick tip.
11551      */
11552        disable : function(){
11553           disabled = true;
11554           clearTimeout(showProc);
11555           clearTimeout(hideProc);
11556           clearTimeout(dismissProc);
11557           if(ce){
11558               hide(true);
11559           }
11560           locks.push(1);
11561        },
11562
11563     /**
11564      * Returns true if the quick tip is enabled, else false.
11565      */
11566        isEnabled : function(){
11567             return !disabled;
11568        },
11569
11570         // private
11571        tagConfig : {
11572            namespace : "roo", // was ext?? this may break..
11573            alt_namespace : "ext",
11574            attribute : "qtip",
11575            width : "width",
11576            target : "target",
11577            title : "qtitle",
11578            hide : "hide",
11579            cls : "qclass"
11580        }
11581    };
11582 }();
11583
11584 // backwards compat
11585 Roo.QuickTips.tips = Roo.QuickTips.register;/*
11586  * Based on:
11587  * Ext JS Library 1.1.1
11588  * Copyright(c) 2006-2007, Ext JS, LLC.
11589  *
11590  * Originally Released Under LGPL - original licence link has changed is not relivant.
11591  *
11592  * Fork - LGPL
11593  * <script type="text/javascript">
11594  */
11595  
11596
11597 /**
11598  * @class Roo.tree.TreePanel
11599  * @extends Roo.data.Tree
11600
11601  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
11602  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
11603  * @cfg {Boolean} enableDD true to enable drag and drop
11604  * @cfg {Boolean} enableDrag true to enable just drag
11605  * @cfg {Boolean} enableDrop true to enable just drop
11606  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
11607  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
11608  * @cfg {String} ddGroup The DD group this TreePanel belongs to
11609  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
11610  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
11611  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
11612  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
11613  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
11614  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
11615  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
11616  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
11617  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
11618  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
11619  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
11620  * @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>
11621  * @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>
11622  * 
11623  * @constructor
11624  * @param {String/HTMLElement/Element} el The container element
11625  * @param {Object} config
11626  */
11627 Roo.tree.TreePanel = function(el, config){
11628     var root = false;
11629     var loader = false;
11630     if (config.root) {
11631         root = config.root;
11632         delete config.root;
11633     }
11634     if (config.loader) {
11635         loader = config.loader;
11636         delete config.loader;
11637     }
11638     
11639     Roo.apply(this, config);
11640     Roo.tree.TreePanel.superclass.constructor.call(this);
11641     this.el = Roo.get(el);
11642     this.el.addClass('x-tree');
11643     //console.log(root);
11644     if (root) {
11645         this.setRootNode( Roo.factory(root, Roo.tree));
11646     }
11647     if (loader) {
11648         this.loader = Roo.factory(loader, Roo.tree);
11649     }
11650    /**
11651     * Read-only. The id of the container element becomes this TreePanel's id.
11652     */
11653     this.id = this.el.id;
11654     this.addEvents({
11655         /**
11656         * @event beforeload
11657         * Fires before a node is loaded, return false to cancel
11658         * @param {Node} node The node being loaded
11659         */
11660         "beforeload" : true,
11661         /**
11662         * @event load
11663         * Fires when a node is loaded
11664         * @param {Node} node The node that was loaded
11665         */
11666         "load" : true,
11667         /**
11668         * @event textchange
11669         * Fires when the text for a node is changed
11670         * @param {Node} node The node
11671         * @param {String} text The new text
11672         * @param {String} oldText The old text
11673         */
11674         "textchange" : true,
11675         /**
11676         * @event beforeexpand
11677         * Fires before a node is expanded, return false to cancel.
11678         * @param {Node} node The node
11679         * @param {Boolean} deep
11680         * @param {Boolean} anim
11681         */
11682         "beforeexpand" : true,
11683         /**
11684         * @event beforecollapse
11685         * Fires before a node is collapsed, return false to cancel.
11686         * @param {Node} node The node
11687         * @param {Boolean} deep
11688         * @param {Boolean} anim
11689         */
11690         "beforecollapse" : true,
11691         /**
11692         * @event expand
11693         * Fires when a node is expanded
11694         * @param {Node} node The node
11695         */
11696         "expand" : true,
11697         /**
11698         * @event disabledchange
11699         * Fires when the disabled status of a node changes
11700         * @param {Node} node The node
11701         * @param {Boolean} disabled
11702         */
11703         "disabledchange" : true,
11704         /**
11705         * @event collapse
11706         * Fires when a node is collapsed
11707         * @param {Node} node The node
11708         */
11709         "collapse" : true,
11710         /**
11711         * @event beforeclick
11712         * Fires before click processing on a node. Return false to cancel the default action.
11713         * @param {Node} node The node
11714         * @param {Roo.EventObject} e The event object
11715         */
11716         "beforeclick":true,
11717         /**
11718         * @event checkchange
11719         * Fires when a node with a checkbox's checked property changes
11720         * @param {Node} this This node
11721         * @param {Boolean} checked
11722         */
11723         "checkchange":true,
11724         /**
11725         * @event click
11726         * Fires when a node is clicked
11727         * @param {Node} node The node
11728         * @param {Roo.EventObject} e The event object
11729         */
11730         "click":true,
11731         /**
11732         * @event dblclick
11733         * Fires when a node is double clicked
11734         * @param {Node} node The node
11735         * @param {Roo.EventObject} e The event object
11736         */
11737         "dblclick":true,
11738         /**
11739         * @event contextmenu
11740         * Fires when a node is right clicked
11741         * @param {Node} node The node
11742         * @param {Roo.EventObject} e The event object
11743         */
11744         "contextmenu":true,
11745         /**
11746         * @event beforechildrenrendered
11747         * Fires right before the child nodes for a node are rendered
11748         * @param {Node} node The node
11749         */
11750         "beforechildrenrendered":true,
11751         /**
11752         * @event startdrag
11753         * Fires when a node starts being dragged
11754         * @param {Roo.tree.TreePanel} this
11755         * @param {Roo.tree.TreeNode} node
11756         * @param {event} e The raw browser event
11757         */ 
11758        "startdrag" : true,
11759        /**
11760         * @event enddrag
11761         * Fires when a drag operation is complete
11762         * @param {Roo.tree.TreePanel} this
11763         * @param {Roo.tree.TreeNode} node
11764         * @param {event} e The raw browser event
11765         */
11766        "enddrag" : true,
11767        /**
11768         * @event dragdrop
11769         * Fires when a dragged node is dropped on a valid DD target
11770         * @param {Roo.tree.TreePanel} this
11771         * @param {Roo.tree.TreeNode} node
11772         * @param {DD} dd The dd it was dropped on
11773         * @param {event} e The raw browser event
11774         */
11775        "dragdrop" : true,
11776        /**
11777         * @event beforenodedrop
11778         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
11779         * passed to handlers has the following properties:<br />
11780         * <ul style="padding:5px;padding-left:16px;">
11781         * <li>tree - The TreePanel</li>
11782         * <li>target - The node being targeted for the drop</li>
11783         * <li>data - The drag data from the drag source</li>
11784         * <li>point - The point of the drop - append, above or below</li>
11785         * <li>source - The drag source</li>
11786         * <li>rawEvent - Raw mouse event</li>
11787         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
11788         * to be inserted by setting them on this object.</li>
11789         * <li>cancel - Set this to true to cancel the drop.</li>
11790         * </ul>
11791         * @param {Object} dropEvent
11792         */
11793        "beforenodedrop" : true,
11794        /**
11795         * @event nodedrop
11796         * Fires after a DD object is dropped on a node in this tree. The dropEvent
11797         * passed to handlers has the following properties:<br />
11798         * <ul style="padding:5px;padding-left:16px;">
11799         * <li>tree - The TreePanel</li>
11800         * <li>target - The node being targeted for the drop</li>
11801         * <li>data - The drag data from the drag source</li>
11802         * <li>point - The point of the drop - append, above or below</li>
11803         * <li>source - The drag source</li>
11804         * <li>rawEvent - Raw mouse event</li>
11805         * <li>dropNode - Dropped node(s).</li>
11806         * </ul>
11807         * @param {Object} dropEvent
11808         */
11809        "nodedrop" : true,
11810         /**
11811         * @event nodedragover
11812         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
11813         * passed to handlers has the following properties:<br />
11814         * <ul style="padding:5px;padding-left:16px;">
11815         * <li>tree - The TreePanel</li>
11816         * <li>target - The node being targeted for the drop</li>
11817         * <li>data - The drag data from the drag source</li>
11818         * <li>point - The point of the drop - append, above or below</li>
11819         * <li>source - The drag source</li>
11820         * <li>rawEvent - Raw mouse event</li>
11821         * <li>dropNode - Drop node(s) provided by the source.</li>
11822         * <li>cancel - Set this to true to signal drop not allowed.</li>
11823         * </ul>
11824         * @param {Object} dragOverEvent
11825         */
11826        "nodedragover" : true
11827         
11828     });
11829     if(this.singleExpand){
11830        this.on("beforeexpand", this.restrictExpand, this);
11831     }
11832     if (this.editor) {
11833         this.editor.tree = this;
11834         this.editor = Roo.factory(this.editor, Roo.tree);
11835     }
11836     
11837     if (this.selModel) {
11838         this.selModel = Roo.factory(this.selModel, Roo.tree);
11839     }
11840    
11841 };
11842 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
11843     rootVisible : true,
11844     animate: Roo.enableFx,
11845     lines : true,
11846     enableDD : false,
11847     hlDrop : Roo.enableFx,
11848   
11849     renderer: false,
11850     
11851     rendererTip: false,
11852     // private
11853     restrictExpand : function(node){
11854         var p = node.parentNode;
11855         if(p){
11856             if(p.expandedChild && p.expandedChild.parentNode == p){
11857                 p.expandedChild.collapse();
11858             }
11859             p.expandedChild = node;
11860         }
11861     },
11862
11863     // private override
11864     setRootNode : function(node){
11865         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
11866         if(!this.rootVisible){
11867             node.ui = new Roo.tree.RootTreeNodeUI(node);
11868         }
11869         return node;
11870     },
11871
11872     /**
11873      * Returns the container element for this TreePanel
11874      */
11875     getEl : function(){
11876         return this.el;
11877     },
11878
11879     /**
11880      * Returns the default TreeLoader for this TreePanel
11881      */
11882     getLoader : function(){
11883         return this.loader;
11884     },
11885
11886     /**
11887      * Expand all nodes
11888      */
11889     expandAll : function(){
11890         this.root.expand(true);
11891     },
11892
11893     /**
11894      * Collapse all nodes
11895      */
11896     collapseAll : function(){
11897         this.root.collapse(true);
11898     },
11899
11900     /**
11901      * Returns the selection model used by this TreePanel
11902      */
11903     getSelectionModel : function(){
11904         if(!this.selModel){
11905             this.selModel = new Roo.tree.DefaultSelectionModel();
11906         }
11907         return this.selModel;
11908     },
11909
11910     /**
11911      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
11912      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
11913      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
11914      * @return {Array}
11915      */
11916     getChecked : function(a, startNode){
11917         startNode = startNode || this.root;
11918         var r = [];
11919         var f = function(){
11920             if(this.attributes.checked){
11921                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
11922             }
11923         }
11924         startNode.cascade(f);
11925         return r;
11926     },
11927
11928     /**
11929      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11930      * @param {String} path
11931      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11932      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
11933      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
11934      */
11935     expandPath : function(path, attr, callback){
11936         attr = attr || "id";
11937         var keys = path.split(this.pathSeparator);
11938         var curNode = this.root;
11939         if(curNode.attributes[attr] != keys[1]){ // invalid root
11940             if(callback){
11941                 callback(false, null);
11942             }
11943             return;
11944         }
11945         var index = 1;
11946         var f = function(){
11947             if(++index == keys.length){
11948                 if(callback){
11949                     callback(true, curNode);
11950                 }
11951                 return;
11952             }
11953             var c = curNode.findChild(attr, keys[index]);
11954             if(!c){
11955                 if(callback){
11956                     callback(false, curNode);
11957                 }
11958                 return;
11959             }
11960             curNode = c;
11961             c.expand(false, false, f);
11962         };
11963         curNode.expand(false, false, f);
11964     },
11965
11966     /**
11967      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11968      * @param {String} path
11969      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11970      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
11971      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
11972      */
11973     selectPath : function(path, attr, callback){
11974         attr = attr || "id";
11975         var keys = path.split(this.pathSeparator);
11976         var v = keys.pop();
11977         if(keys.length > 0){
11978             var f = function(success, node){
11979                 if(success && node){
11980                     var n = node.findChild(attr, v);
11981                     if(n){
11982                         n.select();
11983                         if(callback){
11984                             callback(true, n);
11985                         }
11986                     }else if(callback){
11987                         callback(false, n);
11988                     }
11989                 }else{
11990                     if(callback){
11991                         callback(false, n);
11992                     }
11993                 }
11994             };
11995             this.expandPath(keys.join(this.pathSeparator), attr, f);
11996         }else{
11997             this.root.select();
11998             if(callback){
11999                 callback(true, this.root);
12000             }
12001         }
12002     },
12003
12004     getTreeEl : function(){
12005         return this.el;
12006     },
12007
12008     /**
12009      * Trigger rendering of this TreePanel
12010      */
12011     render : function(){
12012         if (this.innerCt) {
12013             return this; // stop it rendering more than once!!
12014         }
12015         
12016         this.innerCt = this.el.createChild({tag:"ul",
12017                cls:"x-tree-root-ct " +
12018                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
12019
12020         if(this.containerScroll){
12021             Roo.dd.ScrollManager.register(this.el);
12022         }
12023         if((this.enableDD || this.enableDrop) && !this.dropZone){
12024            /**
12025             * The dropZone used by this tree if drop is enabled
12026             * @type Roo.tree.TreeDropZone
12027             */
12028              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
12029                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
12030            });
12031         }
12032         if((this.enableDD || this.enableDrag) && !this.dragZone){
12033            /**
12034             * The dragZone used by this tree if drag is enabled
12035             * @type Roo.tree.TreeDragZone
12036             */
12037             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
12038                ddGroup: this.ddGroup || "TreeDD",
12039                scroll: this.ddScroll
12040            });
12041         }
12042         this.getSelectionModel().init(this);
12043         if (!this.root) {
12044             Roo.log("ROOT not set in tree");
12045             return this;
12046         }
12047         this.root.render();
12048         if(!this.rootVisible){
12049             this.root.renderChildren();
12050         }
12051         return this;
12052     }
12053 });/*
12054  * Based on:
12055  * Ext JS Library 1.1.1
12056  * Copyright(c) 2006-2007, Ext JS, LLC.
12057  *
12058  * Originally Released Under LGPL - original licence link has changed is not relivant.
12059  *
12060  * Fork - LGPL
12061  * <script type="text/javascript">
12062  */
12063  
12064
12065 /**
12066  * @class Roo.tree.DefaultSelectionModel
12067  * @extends Roo.util.Observable
12068  * The default single selection for a TreePanel.
12069  * @param {Object} cfg Configuration
12070  */
12071 Roo.tree.DefaultSelectionModel = function(cfg){
12072    this.selNode = null;
12073    
12074    
12075    
12076    this.addEvents({
12077        /**
12078         * @event selectionchange
12079         * Fires when the selected node changes
12080         * @param {DefaultSelectionModel} this
12081         * @param {TreeNode} node the new selection
12082         */
12083        "selectionchange" : true,
12084
12085        /**
12086         * @event beforeselect
12087         * Fires before the selected node changes, return false to cancel the change
12088         * @param {DefaultSelectionModel} this
12089         * @param {TreeNode} node the new selection
12090         * @param {TreeNode} node the old selection
12091         */
12092        "beforeselect" : true
12093    });
12094    
12095     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
12096 };
12097
12098 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
12099     init : function(tree){
12100         this.tree = tree;
12101         tree.getTreeEl().on("keydown", this.onKeyDown, this);
12102         tree.on("click", this.onNodeClick, this);
12103     },
12104     
12105     onNodeClick : function(node, e){
12106         if (e.ctrlKey && this.selNode == node)  {
12107             this.unselect(node);
12108             return;
12109         }
12110         this.select(node);
12111     },
12112     
12113     /**
12114      * Select a node.
12115      * @param {TreeNode} node The node to select
12116      * @return {TreeNode} The selected node
12117      */
12118     select : function(node){
12119         var last = this.selNode;
12120         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
12121             if(last){
12122                 last.ui.onSelectedChange(false);
12123             }
12124             this.selNode = node;
12125             node.ui.onSelectedChange(true);
12126             this.fireEvent("selectionchange", this, node, last);
12127         }
12128         return node;
12129     },
12130     
12131     /**
12132      * Deselect a node.
12133      * @param {TreeNode} node The node to unselect
12134      */
12135     unselect : function(node){
12136         if(this.selNode == node){
12137             this.clearSelections();
12138         }    
12139     },
12140     
12141     /**
12142      * Clear all selections
12143      */
12144     clearSelections : function(){
12145         var n = this.selNode;
12146         if(n){
12147             n.ui.onSelectedChange(false);
12148             this.selNode = null;
12149             this.fireEvent("selectionchange", this, null);
12150         }
12151         return n;
12152     },
12153     
12154     /**
12155      * Get the selected node
12156      * @return {TreeNode} The selected node
12157      */
12158     getSelectedNode : function(){
12159         return this.selNode;    
12160     },
12161     
12162     /**
12163      * Returns true if the node is selected
12164      * @param {TreeNode} node The node to check
12165      * @return {Boolean}
12166      */
12167     isSelected : function(node){
12168         return this.selNode == node;  
12169     },
12170
12171     /**
12172      * Selects the node above the selected node in the tree, intelligently walking the nodes
12173      * @return TreeNode The new selection
12174      */
12175     selectPrevious : function(){
12176         var s = this.selNode || this.lastSelNode;
12177         if(!s){
12178             return null;
12179         }
12180         var ps = s.previousSibling;
12181         if(ps){
12182             if(!ps.isExpanded() || ps.childNodes.length < 1){
12183                 return this.select(ps);
12184             } else{
12185                 var lc = ps.lastChild;
12186                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
12187                     lc = lc.lastChild;
12188                 }
12189                 return this.select(lc);
12190             }
12191         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
12192             return this.select(s.parentNode);
12193         }
12194         return null;
12195     },
12196
12197     /**
12198      * Selects the node above the selected node in the tree, intelligently walking the nodes
12199      * @return TreeNode The new selection
12200      */
12201     selectNext : function(){
12202         var s = this.selNode || this.lastSelNode;
12203         if(!s){
12204             return null;
12205         }
12206         if(s.firstChild && s.isExpanded()){
12207              return this.select(s.firstChild);
12208          }else if(s.nextSibling){
12209              return this.select(s.nextSibling);
12210          }else if(s.parentNode){
12211             var newS = null;
12212             s.parentNode.bubble(function(){
12213                 if(this.nextSibling){
12214                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
12215                     return false;
12216                 }
12217             });
12218             return newS;
12219          }
12220         return null;
12221     },
12222
12223     onKeyDown : function(e){
12224         var s = this.selNode || this.lastSelNode;
12225         // undesirable, but required
12226         var sm = this;
12227         if(!s){
12228             return;
12229         }
12230         var k = e.getKey();
12231         switch(k){
12232              case e.DOWN:
12233                  e.stopEvent();
12234                  this.selectNext();
12235              break;
12236              case e.UP:
12237                  e.stopEvent();
12238                  this.selectPrevious();
12239              break;
12240              case e.RIGHT:
12241                  e.preventDefault();
12242                  if(s.hasChildNodes()){
12243                      if(!s.isExpanded()){
12244                          s.expand();
12245                      }else if(s.firstChild){
12246                          this.select(s.firstChild, e);
12247                      }
12248                  }
12249              break;
12250              case e.LEFT:
12251                  e.preventDefault();
12252                  if(s.hasChildNodes() && s.isExpanded()){
12253                      s.collapse();
12254                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
12255                      this.select(s.parentNode, e);
12256                  }
12257              break;
12258         };
12259     }
12260 });
12261
12262 /**
12263  * @class Roo.tree.MultiSelectionModel
12264  * @extends Roo.util.Observable
12265  * Multi selection for a TreePanel.
12266  * @param {Object} cfg Configuration
12267  */
12268 Roo.tree.MultiSelectionModel = function(){
12269    this.selNodes = [];
12270    this.selMap = {};
12271    this.addEvents({
12272        /**
12273         * @event selectionchange
12274         * Fires when the selected nodes change
12275         * @param {MultiSelectionModel} this
12276         * @param {Array} nodes Array of the selected nodes
12277         */
12278        "selectionchange" : true
12279    });
12280    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
12281    
12282 };
12283
12284 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
12285     init : function(tree){
12286         this.tree = tree;
12287         tree.getTreeEl().on("keydown", this.onKeyDown, this);
12288         tree.on("click", this.onNodeClick, this);
12289     },
12290     
12291     onNodeClick : function(node, e){
12292         this.select(node, e, e.ctrlKey);
12293     },
12294     
12295     /**
12296      * Select a node.
12297      * @param {TreeNode} node The node to select
12298      * @param {EventObject} e (optional) An event associated with the selection
12299      * @param {Boolean} keepExisting True to retain existing selections
12300      * @return {TreeNode} The selected node
12301      */
12302     select : function(node, e, keepExisting){
12303         if(keepExisting !== true){
12304             this.clearSelections(true);
12305         }
12306         if(this.isSelected(node)){
12307             this.lastSelNode = node;
12308             return node;
12309         }
12310         this.selNodes.push(node);
12311         this.selMap[node.id] = node;
12312         this.lastSelNode = node;
12313         node.ui.onSelectedChange(true);
12314         this.fireEvent("selectionchange", this, this.selNodes);
12315         return node;
12316     },
12317     
12318     /**
12319      * Deselect a node.
12320      * @param {TreeNode} node The node to unselect
12321      */
12322     unselect : function(node){
12323         if(this.selMap[node.id]){
12324             node.ui.onSelectedChange(false);
12325             var sn = this.selNodes;
12326             var index = -1;
12327             if(sn.indexOf){
12328                 index = sn.indexOf(node);
12329             }else{
12330                 for(var i = 0, len = sn.length; i < len; i++){
12331                     if(sn[i] == node){
12332                         index = i;
12333                         break;
12334                     }
12335                 }
12336             }
12337             if(index != -1){
12338                 this.selNodes.splice(index, 1);
12339             }
12340             delete this.selMap[node.id];
12341             this.fireEvent("selectionchange", this, this.selNodes);
12342         }
12343     },
12344     
12345     /**
12346      * Clear all selections
12347      */
12348     clearSelections : function(suppressEvent){
12349         var sn = this.selNodes;
12350         if(sn.length > 0){
12351             for(var i = 0, len = sn.length; i < len; i++){
12352                 sn[i].ui.onSelectedChange(false);
12353             }
12354             this.selNodes = [];
12355             this.selMap = {};
12356             if(suppressEvent !== true){
12357                 this.fireEvent("selectionchange", this, this.selNodes);
12358             }
12359         }
12360     },
12361     
12362     /**
12363      * Returns true if the node is selected
12364      * @param {TreeNode} node The node to check
12365      * @return {Boolean}
12366      */
12367     isSelected : function(node){
12368         return this.selMap[node.id] ? true : false;  
12369     },
12370     
12371     /**
12372      * Returns an array of the selected nodes
12373      * @return {Array}
12374      */
12375     getSelectedNodes : function(){
12376         return this.selNodes;    
12377     },
12378
12379     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
12380
12381     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
12382
12383     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
12384 });/*
12385  * Based on:
12386  * Ext JS Library 1.1.1
12387  * Copyright(c) 2006-2007, Ext JS, LLC.
12388  *
12389  * Originally Released Under LGPL - original licence link has changed is not relivant.
12390  *
12391  * Fork - LGPL
12392  * <script type="text/javascript">
12393  */
12394  
12395 /**
12396  * @class Roo.tree.TreeNode
12397  * @extends Roo.data.Node
12398  * @cfg {String} text The text for this node
12399  * @cfg {Boolean} expanded true to start the node expanded
12400  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
12401  * @cfg {Boolean} allowDrop false if this node cannot be drop on
12402  * @cfg {Boolean} disabled true to start the node disabled
12403  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
12404  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
12405  * @cfg {String} cls A css class to be added to the node
12406  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
12407  * @cfg {String} href URL of the link used for the node (defaults to #)
12408  * @cfg {String} hrefTarget target frame for the link
12409  * @cfg {String} qtip An Ext QuickTip for the node
12410  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
12411  * @cfg {Boolean} singleClickExpand True for single click expand on this node
12412  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
12413  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
12414  * (defaults to undefined with no checkbox rendered)
12415  * @constructor
12416  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
12417  */
12418 Roo.tree.TreeNode = function(attributes){
12419     attributes = attributes || {};
12420     if(typeof attributes == "string"){
12421         attributes = {text: attributes};
12422     }
12423     this.childrenRendered = false;
12424     this.rendered = false;
12425     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
12426     this.expanded = attributes.expanded === true;
12427     this.isTarget = attributes.isTarget !== false;
12428     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
12429     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
12430
12431     /**
12432      * Read-only. The text for this node. To change it use setText().
12433      * @type String
12434      */
12435     this.text = attributes.text;
12436     /**
12437      * True if this node is disabled.
12438      * @type Boolean
12439      */
12440     this.disabled = attributes.disabled === true;
12441
12442     this.addEvents({
12443         /**
12444         * @event textchange
12445         * Fires when the text for this node is changed
12446         * @param {Node} this This node
12447         * @param {String} text The new text
12448         * @param {String} oldText The old text
12449         */
12450         "textchange" : true,
12451         /**
12452         * @event beforeexpand
12453         * Fires before this node is expanded, return false to cancel.
12454         * @param {Node} this This node
12455         * @param {Boolean} deep
12456         * @param {Boolean} anim
12457         */
12458         "beforeexpand" : true,
12459         /**
12460         * @event beforecollapse
12461         * Fires before this node is collapsed, return false to cancel.
12462         * @param {Node} this This node
12463         * @param {Boolean} deep
12464         * @param {Boolean} anim
12465         */
12466         "beforecollapse" : true,
12467         /**
12468         * @event expand
12469         * Fires when this node is expanded
12470         * @param {Node} this This node
12471         */
12472         "expand" : true,
12473         /**
12474         * @event disabledchange
12475         * Fires when the disabled status of this node changes
12476         * @param {Node} this This node
12477         * @param {Boolean} disabled
12478         */
12479         "disabledchange" : true,
12480         /**
12481         * @event collapse
12482         * Fires when this node is collapsed
12483         * @param {Node} this This node
12484         */
12485         "collapse" : true,
12486         /**
12487         * @event beforeclick
12488         * Fires before click processing. Return false to cancel the default action.
12489         * @param {Node} this This node
12490         * @param {Roo.EventObject} e The event object
12491         */
12492         "beforeclick":true,
12493         /**
12494         * @event checkchange
12495         * Fires when a node with a checkbox's checked property changes
12496         * @param {Node} this This node
12497         * @param {Boolean} checked
12498         */
12499         "checkchange":true,
12500         /**
12501         * @event click
12502         * Fires when this node is clicked
12503         * @param {Node} this This node
12504         * @param {Roo.EventObject} e The event object
12505         */
12506         "click":true,
12507         /**
12508         * @event dblclick
12509         * Fires when this node is double clicked
12510         * @param {Node} this This node
12511         * @param {Roo.EventObject} e The event object
12512         */
12513         "dblclick":true,
12514         /**
12515         * @event contextmenu
12516         * Fires when this node is right clicked
12517         * @param {Node} this This node
12518         * @param {Roo.EventObject} e The event object
12519         */
12520         "contextmenu":true,
12521         /**
12522         * @event beforechildrenrendered
12523         * Fires right before the child nodes for this node are rendered
12524         * @param {Node} this This node
12525         */
12526         "beforechildrenrendered":true
12527     });
12528
12529     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
12530
12531     /**
12532      * Read-only. The UI for this node
12533      * @type TreeNodeUI
12534      */
12535     this.ui = new uiClass(this);
12536     
12537     // finally support items[]
12538     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
12539         return;
12540     }
12541     
12542     
12543     Roo.each(this.attributes.items, function(c) {
12544         this.appendChild(Roo.factory(c,Roo.Tree));
12545     }, this);
12546     delete this.attributes.items;
12547     
12548     
12549     
12550 };
12551 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
12552     preventHScroll: true,
12553     /**
12554      * Returns true if this node is expanded
12555      * @return {Boolean}
12556      */
12557     isExpanded : function(){
12558         return this.expanded;
12559     },
12560
12561     /**
12562      * Returns the UI object for this node
12563      * @return {TreeNodeUI}
12564      */
12565     getUI : function(){
12566         return this.ui;
12567     },
12568
12569     // private override
12570     setFirstChild : function(node){
12571         var of = this.firstChild;
12572         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
12573         if(this.childrenRendered && of && node != of){
12574             of.renderIndent(true, true);
12575         }
12576         if(this.rendered){
12577             this.renderIndent(true, true);
12578         }
12579     },
12580
12581     // private override
12582     setLastChild : function(node){
12583         var ol = this.lastChild;
12584         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
12585         if(this.childrenRendered && ol && node != ol){
12586             ol.renderIndent(true, true);
12587         }
12588         if(this.rendered){
12589             this.renderIndent(true, true);
12590         }
12591     },
12592
12593     // these methods are overridden to provide lazy rendering support
12594     // private override
12595     appendChild : function()
12596     {
12597         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
12598         if(node && this.childrenRendered){
12599             node.render();
12600         }
12601         this.ui.updateExpandIcon();
12602         return node;
12603     },
12604
12605     // private override
12606     removeChild : function(node){
12607         this.ownerTree.getSelectionModel().unselect(node);
12608         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
12609         // if it's been rendered remove dom node
12610         if(this.childrenRendered){
12611             node.ui.remove();
12612         }
12613         if(this.childNodes.length < 1){
12614             this.collapse(false, false);
12615         }else{
12616             this.ui.updateExpandIcon();
12617         }
12618         if(!this.firstChild) {
12619             this.childrenRendered = false;
12620         }
12621         return node;
12622     },
12623
12624     // private override
12625     insertBefore : function(node, refNode){
12626         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
12627         if(newNode && refNode && this.childrenRendered){
12628             node.render();
12629         }
12630         this.ui.updateExpandIcon();
12631         return newNode;
12632     },
12633
12634     /**
12635      * Sets the text for this node
12636      * @param {String} text
12637      */
12638     setText : function(text){
12639         var oldText = this.text;
12640         this.text = text;
12641         this.attributes.text = text;
12642         if(this.rendered){ // event without subscribing
12643             this.ui.onTextChange(this, text, oldText);
12644         }
12645         this.fireEvent("textchange", this, text, oldText);
12646     },
12647
12648     /**
12649      * Triggers selection of this node
12650      */
12651     select : function(){
12652         this.getOwnerTree().getSelectionModel().select(this);
12653     },
12654
12655     /**
12656      * Triggers deselection of this node
12657      */
12658     unselect : function(){
12659         this.getOwnerTree().getSelectionModel().unselect(this);
12660     },
12661
12662     /**
12663      * Returns true if this node is selected
12664      * @return {Boolean}
12665      */
12666     isSelected : function(){
12667         return this.getOwnerTree().getSelectionModel().isSelected(this);
12668     },
12669
12670     /**
12671      * Expand this node.
12672      * @param {Boolean} deep (optional) True to expand all children as well
12673      * @param {Boolean} anim (optional) false to cancel the default animation
12674      * @param {Function} callback (optional) A callback to be called when
12675      * expanding this node completes (does not wait for deep expand to complete).
12676      * Called with 1 parameter, this node.
12677      */
12678     expand : function(deep, anim, callback){
12679         if(!this.expanded){
12680             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
12681                 return;
12682             }
12683             if(!this.childrenRendered){
12684                 this.renderChildren();
12685             }
12686             this.expanded = true;
12687             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
12688                 this.ui.animExpand(function(){
12689                     this.fireEvent("expand", this);
12690                     if(typeof callback == "function"){
12691                         callback(this);
12692                     }
12693                     if(deep === true){
12694                         this.expandChildNodes(true);
12695                     }
12696                 }.createDelegate(this));
12697                 return;
12698             }else{
12699                 this.ui.expand();
12700                 this.fireEvent("expand", this);
12701                 if(typeof callback == "function"){
12702                     callback(this);
12703                 }
12704             }
12705         }else{
12706            if(typeof callback == "function"){
12707                callback(this);
12708            }
12709         }
12710         if(deep === true){
12711             this.expandChildNodes(true);
12712         }
12713     },
12714
12715     isHiddenRoot : function(){
12716         return this.isRoot && !this.getOwnerTree().rootVisible;
12717     },
12718
12719     /**
12720      * Collapse this node.
12721      * @param {Boolean} deep (optional) True to collapse all children as well
12722      * @param {Boolean} anim (optional) false to cancel the default animation
12723      */
12724     collapse : function(deep, anim){
12725         if(this.expanded && !this.isHiddenRoot()){
12726             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
12727                 return;
12728             }
12729             this.expanded = false;
12730             if((this.getOwnerTree().animate && anim !== false) || anim){
12731                 this.ui.animCollapse(function(){
12732                     this.fireEvent("collapse", this);
12733                     if(deep === true){
12734                         this.collapseChildNodes(true);
12735                     }
12736                 }.createDelegate(this));
12737                 return;
12738             }else{
12739                 this.ui.collapse();
12740                 this.fireEvent("collapse", this);
12741             }
12742         }
12743         if(deep === true){
12744             var cs = this.childNodes;
12745             for(var i = 0, len = cs.length; i < len; i++) {
12746                 cs[i].collapse(true, false);
12747             }
12748         }
12749     },
12750
12751     // private
12752     delayedExpand : function(delay){
12753         if(!this.expandProcId){
12754             this.expandProcId = this.expand.defer(delay, this);
12755         }
12756     },
12757
12758     // private
12759     cancelExpand : function(){
12760         if(this.expandProcId){
12761             clearTimeout(this.expandProcId);
12762         }
12763         this.expandProcId = false;
12764     },
12765
12766     /**
12767      * Toggles expanded/collapsed state of the node
12768      */
12769     toggle : function(){
12770         if(this.expanded){
12771             this.collapse();
12772         }else{
12773             this.expand();
12774         }
12775     },
12776
12777     /**
12778      * Ensures all parent nodes are expanded
12779      */
12780     ensureVisible : function(callback){
12781         var tree = this.getOwnerTree();
12782         tree.expandPath(this.parentNode.getPath(), false, function(){
12783             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
12784             Roo.callback(callback);
12785         }.createDelegate(this));
12786     },
12787
12788     /**
12789      * Expand all child nodes
12790      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
12791      */
12792     expandChildNodes : function(deep){
12793         var cs = this.childNodes;
12794         for(var i = 0, len = cs.length; i < len; i++) {
12795                 cs[i].expand(deep);
12796         }
12797     },
12798
12799     /**
12800      * Collapse all child nodes
12801      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
12802      */
12803     collapseChildNodes : function(deep){
12804         var cs = this.childNodes;
12805         for(var i = 0, len = cs.length; i < len; i++) {
12806                 cs[i].collapse(deep);
12807         }
12808     },
12809
12810     /**
12811      * Disables this node
12812      */
12813     disable : function(){
12814         this.disabled = true;
12815         this.unselect();
12816         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12817             this.ui.onDisableChange(this, true);
12818         }
12819         this.fireEvent("disabledchange", this, true);
12820     },
12821
12822     /**
12823      * Enables this node
12824      */
12825     enable : function(){
12826         this.disabled = false;
12827         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12828             this.ui.onDisableChange(this, false);
12829         }
12830         this.fireEvent("disabledchange", this, false);
12831     },
12832
12833     // private
12834     renderChildren : function(suppressEvent){
12835         if(suppressEvent !== false){
12836             this.fireEvent("beforechildrenrendered", this);
12837         }
12838         var cs = this.childNodes;
12839         for(var i = 0, len = cs.length; i < len; i++){
12840             cs[i].render(true);
12841         }
12842         this.childrenRendered = true;
12843     },
12844
12845     // private
12846     sort : function(fn, scope){
12847         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
12848         if(this.childrenRendered){
12849             var cs = this.childNodes;
12850             for(var i = 0, len = cs.length; i < len; i++){
12851                 cs[i].render(true);
12852             }
12853         }
12854     },
12855
12856     // private
12857     render : function(bulkRender){
12858         this.ui.render(bulkRender);
12859         if(!this.rendered){
12860             this.rendered = true;
12861             if(this.expanded){
12862                 this.expanded = false;
12863                 this.expand(false, false);
12864             }
12865         }
12866     },
12867
12868     // private
12869     renderIndent : function(deep, refresh){
12870         if(refresh){
12871             this.ui.childIndent = null;
12872         }
12873         this.ui.renderIndent();
12874         if(deep === true && this.childrenRendered){
12875             var cs = this.childNodes;
12876             for(var i = 0, len = cs.length; i < len; i++){
12877                 cs[i].renderIndent(true, refresh);
12878             }
12879         }
12880     }
12881 });/*
12882  * Based on:
12883  * Ext JS Library 1.1.1
12884  * Copyright(c) 2006-2007, Ext JS, LLC.
12885  *
12886  * Originally Released Under LGPL - original licence link has changed is not relivant.
12887  *
12888  * Fork - LGPL
12889  * <script type="text/javascript">
12890  */
12891  
12892 /**
12893  * @class Roo.tree.AsyncTreeNode
12894  * @extends Roo.tree.TreeNode
12895  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
12896  * @constructor
12897  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
12898  */
12899  Roo.tree.AsyncTreeNode = function(config){
12900     this.loaded = false;
12901     this.loading = false;
12902     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
12903     /**
12904     * @event beforeload
12905     * Fires before this node is loaded, return false to cancel
12906     * @param {Node} this This node
12907     */
12908     this.addEvents({'beforeload':true, 'load': true});
12909     /**
12910     * @event load
12911     * Fires when this node is loaded
12912     * @param {Node} this This node
12913     */
12914     /**
12915      * The loader used by this node (defaults to using the tree's defined loader)
12916      * @type TreeLoader
12917      * @property loader
12918      */
12919 };
12920 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
12921     expand : function(deep, anim, callback){
12922         if(this.loading){ // if an async load is already running, waiting til it's done
12923             var timer;
12924             var f = function(){
12925                 if(!this.loading){ // done loading
12926                     clearInterval(timer);
12927                     this.expand(deep, anim, callback);
12928                 }
12929             }.createDelegate(this);
12930             timer = setInterval(f, 200);
12931             return;
12932         }
12933         if(!this.loaded){
12934             if(this.fireEvent("beforeload", this) === false){
12935                 return;
12936             }
12937             this.loading = true;
12938             this.ui.beforeLoad(this);
12939             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
12940             if(loader){
12941                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
12942                 return;
12943             }
12944         }
12945         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
12946     },
12947     
12948     /**
12949      * Returns true if this node is currently loading
12950      * @return {Boolean}
12951      */
12952     isLoading : function(){
12953         return this.loading;  
12954     },
12955     
12956     loadComplete : function(deep, anim, callback){
12957         this.loading = false;
12958         this.loaded = true;
12959         this.ui.afterLoad(this);
12960         this.fireEvent("load", this);
12961         this.expand(deep, anim, callback);
12962     },
12963     
12964     /**
12965      * Returns true if this node has been loaded
12966      * @return {Boolean}
12967      */
12968     isLoaded : function(){
12969         return this.loaded;
12970     },
12971     
12972     hasChildNodes : function(){
12973         if(!this.isLeaf() && !this.loaded){
12974             return true;
12975         }else{
12976             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
12977         }
12978     },
12979
12980     /**
12981      * Trigger a reload for this node
12982      * @param {Function} callback
12983      */
12984     reload : function(callback){
12985         this.collapse(false, false);
12986         while(this.firstChild){
12987             this.removeChild(this.firstChild);
12988         }
12989         this.childrenRendered = false;
12990         this.loaded = false;
12991         if(this.isHiddenRoot()){
12992             this.expanded = false;
12993         }
12994         this.expand(false, false, callback);
12995     }
12996 });/*
12997  * Based on:
12998  * Ext JS Library 1.1.1
12999  * Copyright(c) 2006-2007, Ext JS, LLC.
13000  *
13001  * Originally Released Under LGPL - original licence link has changed is not relivant.
13002  *
13003  * Fork - LGPL
13004  * <script type="text/javascript">
13005  */
13006  
13007 /**
13008  * @class Roo.tree.TreeNodeUI
13009  * @constructor
13010  * @param {Object} node The node to render
13011  * The TreeNode UI implementation is separate from the
13012  * tree implementation. Unless you are customizing the tree UI,
13013  * you should never have to use this directly.
13014  */
13015 Roo.tree.TreeNodeUI = function(node){
13016     this.node = node;
13017     this.rendered = false;
13018     this.animating = false;
13019     this.emptyIcon = Roo.BLANK_IMAGE_URL;
13020 };
13021
13022 Roo.tree.TreeNodeUI.prototype = {
13023     removeChild : function(node){
13024         if(this.rendered){
13025             this.ctNode.removeChild(node.ui.getEl());
13026         }
13027     },
13028
13029     beforeLoad : function(){
13030          this.addClass("x-tree-node-loading");
13031     },
13032
13033     afterLoad : function(){
13034          this.removeClass("x-tree-node-loading");
13035     },
13036
13037     onTextChange : function(node, text, oldText){
13038         if(this.rendered){
13039             this.textNode.innerHTML = text;
13040         }
13041     },
13042
13043     onDisableChange : function(node, state){
13044         this.disabled = state;
13045         if(state){
13046             this.addClass("x-tree-node-disabled");
13047         }else{
13048             this.removeClass("x-tree-node-disabled");
13049         }
13050     },
13051
13052     onSelectedChange : function(state){
13053         if(state){
13054             this.focus();
13055             this.addClass("x-tree-selected");
13056         }else{
13057             //this.blur();
13058             this.removeClass("x-tree-selected");
13059         }
13060     },
13061
13062     onMove : function(tree, node, oldParent, newParent, index, refNode){
13063         this.childIndent = null;
13064         if(this.rendered){
13065             var targetNode = newParent.ui.getContainer();
13066             if(!targetNode){//target not rendered
13067                 this.holder = document.createElement("div");
13068                 this.holder.appendChild(this.wrap);
13069                 return;
13070             }
13071             var insertBefore = refNode ? refNode.ui.getEl() : null;
13072             if(insertBefore){
13073                 targetNode.insertBefore(this.wrap, insertBefore);
13074             }else{
13075                 targetNode.appendChild(this.wrap);
13076             }
13077             this.node.renderIndent(true);
13078         }
13079     },
13080
13081     addClass : function(cls){
13082         if(this.elNode){
13083             Roo.fly(this.elNode).addClass(cls);
13084         }
13085     },
13086
13087     removeClass : function(cls){
13088         if(this.elNode){
13089             Roo.fly(this.elNode).removeClass(cls);
13090         }
13091     },
13092
13093     remove : function(){
13094         if(this.rendered){
13095             this.holder = document.createElement("div");
13096             this.holder.appendChild(this.wrap);
13097         }
13098     },
13099
13100     fireEvent : function(){
13101         return this.node.fireEvent.apply(this.node, arguments);
13102     },
13103
13104     initEvents : function(){
13105         this.node.on("move", this.onMove, this);
13106         var E = Roo.EventManager;
13107         var a = this.anchor;
13108
13109         var el = Roo.fly(a, '_treeui');
13110
13111         if(Roo.isOpera){ // opera render bug ignores the CSS
13112             el.setStyle("text-decoration", "none");
13113         }
13114
13115         el.on("click", this.onClick, this);
13116         el.on("dblclick", this.onDblClick, this);
13117
13118         if(this.checkbox){
13119             Roo.EventManager.on(this.checkbox,
13120                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
13121         }
13122
13123         el.on("contextmenu", this.onContextMenu, this);
13124
13125         var icon = Roo.fly(this.iconNode);
13126         icon.on("click", this.onClick, this);
13127         icon.on("dblclick", this.onDblClick, this);
13128         icon.on("contextmenu", this.onContextMenu, this);
13129         E.on(this.ecNode, "click", this.ecClick, this, true);
13130
13131         if(this.node.disabled){
13132             this.addClass("x-tree-node-disabled");
13133         }
13134         if(this.node.hidden){
13135             this.addClass("x-tree-node-disabled");
13136         }
13137         var ot = this.node.getOwnerTree();
13138         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
13139         if(dd && (!this.node.isRoot || ot.rootVisible)){
13140             Roo.dd.Registry.register(this.elNode, {
13141                 node: this.node,
13142                 handles: this.getDDHandles(),
13143                 isHandle: false
13144             });
13145         }
13146     },
13147
13148     getDDHandles : function(){
13149         return [this.iconNode, this.textNode];
13150     },
13151
13152     hide : function(){
13153         if(this.rendered){
13154             this.wrap.style.display = "none";
13155         }
13156     },
13157
13158     show : function(){
13159         if(this.rendered){
13160             this.wrap.style.display = "";
13161         }
13162     },
13163
13164     onContextMenu : function(e){
13165         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
13166             e.preventDefault();
13167             this.focus();
13168             this.fireEvent("contextmenu", this.node, e);
13169         }
13170     },
13171
13172     onClick : function(e){
13173         if(this.dropping){
13174             e.stopEvent();
13175             return;
13176         }
13177         if(this.fireEvent("beforeclick", this.node, e) !== false){
13178             if(!this.disabled && this.node.attributes.href){
13179                 this.fireEvent("click", this.node, e);
13180                 return;
13181             }
13182             e.preventDefault();
13183             if(this.disabled){
13184                 return;
13185             }
13186
13187             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
13188                 this.node.toggle();
13189             }
13190
13191             this.fireEvent("click", this.node, e);
13192         }else{
13193             e.stopEvent();
13194         }
13195     },
13196
13197     onDblClick : function(e){
13198         e.preventDefault();
13199         if(this.disabled){
13200             return;
13201         }
13202         if(this.checkbox){
13203             this.toggleCheck();
13204         }
13205         if(!this.animating && this.node.hasChildNodes()){
13206             this.node.toggle();
13207         }
13208         this.fireEvent("dblclick", this.node, e);
13209     },
13210
13211     onCheckChange : function(){
13212         var checked = this.checkbox.checked;
13213         this.node.attributes.checked = checked;
13214         this.fireEvent('checkchange', this.node, checked);
13215     },
13216
13217     ecClick : function(e){
13218         if(!this.animating && this.node.hasChildNodes()){
13219             this.node.toggle();
13220         }
13221     },
13222
13223     startDrop : function(){
13224         this.dropping = true;
13225     },
13226
13227     // delayed drop so the click event doesn't get fired on a drop
13228     endDrop : function(){
13229        setTimeout(function(){
13230            this.dropping = false;
13231        }.createDelegate(this), 50);
13232     },
13233
13234     expand : function(){
13235         this.updateExpandIcon();
13236         this.ctNode.style.display = "";
13237     },
13238
13239     focus : function(){
13240         if(!this.node.preventHScroll){
13241             try{this.anchor.focus();
13242             }catch(e){}
13243         }else if(!Roo.isIE){
13244             try{
13245                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
13246                 var l = noscroll.scrollLeft;
13247                 this.anchor.focus();
13248                 noscroll.scrollLeft = l;
13249             }catch(e){}
13250         }
13251     },
13252
13253     toggleCheck : function(value){
13254         var cb = this.checkbox;
13255         if(cb){
13256             cb.checked = (value === undefined ? !cb.checked : value);
13257         }
13258     },
13259
13260     blur : function(){
13261         try{
13262             this.anchor.blur();
13263         }catch(e){}
13264     },
13265
13266     animExpand : function(callback){
13267         var ct = Roo.get(this.ctNode);
13268         ct.stopFx();
13269         if(!this.node.hasChildNodes()){
13270             this.updateExpandIcon();
13271             this.ctNode.style.display = "";
13272             Roo.callback(callback);
13273             return;
13274         }
13275         this.animating = true;
13276         this.updateExpandIcon();
13277
13278         ct.slideIn('t', {
13279            callback : function(){
13280                this.animating = false;
13281                Roo.callback(callback);
13282             },
13283             scope: this,
13284             duration: this.node.ownerTree.duration || .25
13285         });
13286     },
13287
13288     highlight : function(){
13289         var tree = this.node.getOwnerTree();
13290         Roo.fly(this.wrap).highlight(
13291             tree.hlColor || "C3DAF9",
13292             {endColor: tree.hlBaseColor}
13293         );
13294     },
13295
13296     collapse : function(){
13297         this.updateExpandIcon();
13298         this.ctNode.style.display = "none";
13299     },
13300
13301     animCollapse : function(callback){
13302         var ct = Roo.get(this.ctNode);
13303         ct.enableDisplayMode('block');
13304         ct.stopFx();
13305
13306         this.animating = true;
13307         this.updateExpandIcon();
13308
13309         ct.slideOut('t', {
13310             callback : function(){
13311                this.animating = false;
13312                Roo.callback(callback);
13313             },
13314             scope: this,
13315             duration: this.node.ownerTree.duration || .25
13316         });
13317     },
13318
13319     getContainer : function(){
13320         return this.ctNode;
13321     },
13322
13323     getEl : function(){
13324         return this.wrap;
13325     },
13326
13327     appendDDGhost : function(ghostNode){
13328         ghostNode.appendChild(this.elNode.cloneNode(true));
13329     },
13330
13331     getDDRepairXY : function(){
13332         return Roo.lib.Dom.getXY(this.iconNode);
13333     },
13334
13335     onRender : function(){
13336         this.render();
13337     },
13338
13339     render : function(bulkRender){
13340         var n = this.node, a = n.attributes;
13341         var targetNode = n.parentNode ?
13342               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
13343
13344         if(!this.rendered){
13345             this.rendered = true;
13346
13347             this.renderElements(n, a, targetNode, bulkRender);
13348
13349             if(a.qtip){
13350                if(this.textNode.setAttributeNS){
13351                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
13352                    if(a.qtipTitle){
13353                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
13354                    }
13355                }else{
13356                    this.textNode.setAttribute("ext:qtip", a.qtip);
13357                    if(a.qtipTitle){
13358                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
13359                    }
13360                }
13361             }else if(a.qtipCfg){
13362                 a.qtipCfg.target = Roo.id(this.textNode);
13363                 Roo.QuickTips.register(a.qtipCfg);
13364             }
13365             this.initEvents();
13366             if(!this.node.expanded){
13367                 this.updateExpandIcon();
13368             }
13369         }else{
13370             if(bulkRender === true) {
13371                 targetNode.appendChild(this.wrap);
13372             }
13373         }
13374     },
13375
13376     renderElements : function(n, a, targetNode, bulkRender)
13377     {
13378         // add some indent caching, this helps performance when rendering a large tree
13379         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
13380         var t = n.getOwnerTree();
13381         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
13382         if (typeof(n.attributes.html) != 'undefined') {
13383             txt = n.attributes.html;
13384         }
13385         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
13386         var cb = typeof a.checked == 'boolean';
13387         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
13388         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
13389             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
13390             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
13391             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
13392             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
13393             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
13394              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
13395                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
13396             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
13397             "</li>"];
13398
13399         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
13400             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
13401                                 n.nextSibling.ui.getEl(), buf.join(""));
13402         }else{
13403             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
13404         }
13405
13406         this.elNode = this.wrap.childNodes[0];
13407         this.ctNode = this.wrap.childNodes[1];
13408         var cs = this.elNode.childNodes;
13409         this.indentNode = cs[0];
13410         this.ecNode = cs[1];
13411         this.iconNode = cs[2];
13412         var index = 3;
13413         if(cb){
13414             this.checkbox = cs[3];
13415             index++;
13416         }
13417         this.anchor = cs[index];
13418         this.textNode = cs[index].firstChild;
13419     },
13420
13421     getAnchor : function(){
13422         return this.anchor;
13423     },
13424
13425     getTextEl : function(){
13426         return this.textNode;
13427     },
13428
13429     getIconEl : function(){
13430         return this.iconNode;
13431     },
13432
13433     isChecked : function(){
13434         return this.checkbox ? this.checkbox.checked : false;
13435     },
13436
13437     updateExpandIcon : function(){
13438         if(this.rendered){
13439             var n = this.node, c1, c2;
13440             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
13441             var hasChild = n.hasChildNodes();
13442             if(hasChild){
13443                 if(n.expanded){
13444                     cls += "-minus";
13445                     c1 = "x-tree-node-collapsed";
13446                     c2 = "x-tree-node-expanded";
13447                 }else{
13448                     cls += "-plus";
13449                     c1 = "x-tree-node-expanded";
13450                     c2 = "x-tree-node-collapsed";
13451                 }
13452                 if(this.wasLeaf){
13453                     this.removeClass("x-tree-node-leaf");
13454                     this.wasLeaf = false;
13455                 }
13456                 if(this.c1 != c1 || this.c2 != c2){
13457                     Roo.fly(this.elNode).replaceClass(c1, c2);
13458                     this.c1 = c1; this.c2 = c2;
13459                 }
13460             }else{
13461                 // this changes non-leafs into leafs if they have no children.
13462                 // it's not very rational behaviour..
13463                 
13464                 if(!this.wasLeaf && this.node.leaf){
13465                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
13466                     delete this.c1;
13467                     delete this.c2;
13468                     this.wasLeaf = true;
13469                 }
13470             }
13471             var ecc = "x-tree-ec-icon "+cls;
13472             if(this.ecc != ecc){
13473                 this.ecNode.className = ecc;
13474                 this.ecc = ecc;
13475             }
13476         }
13477     },
13478
13479     getChildIndent : function(){
13480         if(!this.childIndent){
13481             var buf = [];
13482             var p = this.node;
13483             while(p){
13484                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
13485                     if(!p.isLast()) {
13486                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
13487                     } else {
13488                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
13489                     }
13490                 }
13491                 p = p.parentNode;
13492             }
13493             this.childIndent = buf.join("");
13494         }
13495         return this.childIndent;
13496     },
13497
13498     renderIndent : function(){
13499         if(this.rendered){
13500             var indent = "";
13501             var p = this.node.parentNode;
13502             if(p){
13503                 indent = p.ui.getChildIndent();
13504             }
13505             if(this.indentMarkup != indent){ // don't rerender if not required
13506                 this.indentNode.innerHTML = indent;
13507                 this.indentMarkup = indent;
13508             }
13509             this.updateExpandIcon();
13510         }
13511     }
13512 };
13513
13514 Roo.tree.RootTreeNodeUI = function(){
13515     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
13516 };
13517 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
13518     render : function(){
13519         if(!this.rendered){
13520             var targetNode = this.node.ownerTree.innerCt.dom;
13521             this.node.expanded = true;
13522             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
13523             this.wrap = this.ctNode = targetNode.firstChild;
13524         }
13525     },
13526     collapse : function(){
13527     },
13528     expand : function(){
13529     }
13530 });/*
13531  * Based on:
13532  * Ext JS Library 1.1.1
13533  * Copyright(c) 2006-2007, Ext JS, LLC.
13534  *
13535  * Originally Released Under LGPL - original licence link has changed is not relivant.
13536  *
13537  * Fork - LGPL
13538  * <script type="text/javascript">
13539  */
13540 /**
13541  * @class Roo.tree.TreeLoader
13542  * @extends Roo.util.Observable
13543  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
13544  * nodes from a specified URL. The response must be a javascript Array definition
13545  * who's elements are node definition objects. eg:
13546  * <pre><code>
13547 {  success : true,
13548    data :      [
13549    
13550     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
13551     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
13552     ]
13553 }
13554
13555
13556 </code></pre>
13557  * <br><br>
13558  * The old style respose with just an array is still supported, but not recommended.
13559  * <br><br>
13560  *
13561  * A server request is sent, and child nodes are loaded only when a node is expanded.
13562  * The loading node's id is passed to the server under the parameter name "node" to
13563  * enable the server to produce the correct child nodes.
13564  * <br><br>
13565  * To pass extra parameters, an event handler may be attached to the "beforeload"
13566  * event, and the parameters specified in the TreeLoader's baseParams property:
13567  * <pre><code>
13568     myTreeLoader.on("beforeload", function(treeLoader, node) {
13569         this.baseParams.category = node.attributes.category;
13570     }, this);
13571     
13572 </code></pre>
13573  *
13574  * This would pass an HTTP parameter called "category" to the server containing
13575  * the value of the Node's "category" attribute.
13576  * @constructor
13577  * Creates a new Treeloader.
13578  * @param {Object} config A config object containing config properties.
13579  */
13580 Roo.tree.TreeLoader = function(config){
13581     this.baseParams = {};
13582     this.requestMethod = "POST";
13583     Roo.apply(this, config);
13584
13585     this.addEvents({
13586     
13587         /**
13588          * @event beforeload
13589          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
13590          * @param {Object} This TreeLoader object.
13591          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13592          * @param {Object} callback The callback function specified in the {@link #load} call.
13593          */
13594         beforeload : true,
13595         /**
13596          * @event load
13597          * Fires when the node has been successfuly loaded.
13598          * @param {Object} This TreeLoader object.
13599          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13600          * @param {Object} response The response object containing the data from the server.
13601          */
13602         load : true,
13603         /**
13604          * @event loadexception
13605          * Fires if the network request failed.
13606          * @param {Object} This TreeLoader object.
13607          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13608          * @param {Object} response The response object containing the data from the server.
13609          */
13610         loadexception : true,
13611         /**
13612          * @event create
13613          * Fires before a node is created, enabling you to return custom Node types 
13614          * @param {Object} This TreeLoader object.
13615          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
13616          */
13617         create : true
13618     });
13619
13620     Roo.tree.TreeLoader.superclass.constructor.call(this);
13621 };
13622
13623 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
13624     /**
13625     * @cfg {String} dataUrl The URL from which to request a Json string which
13626     * specifies an array of node definition object representing the child nodes
13627     * to be loaded.
13628     */
13629     /**
13630     * @cfg {String} requestMethod either GET or POST
13631     * defaults to POST (due to BC)
13632     * to be loaded.
13633     */
13634     /**
13635     * @cfg {Object} baseParams (optional) An object containing properties which
13636     * specify HTTP parameters to be passed to each request for child nodes.
13637     */
13638     /**
13639     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
13640     * created by this loader. If the attributes sent by the server have an attribute in this object,
13641     * they take priority.
13642     */
13643     /**
13644     * @cfg {Object} uiProviders (optional) An object containing properties which
13645     * 
13646     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
13647     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
13648     * <i>uiProvider</i> attribute of a returned child node is a string rather
13649     * than a reference to a TreeNodeUI implementation, this that string value
13650     * is used as a property name in the uiProviders object. You can define the provider named
13651     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
13652     */
13653     uiProviders : {},
13654
13655     /**
13656     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
13657     * child nodes before loading.
13658     */
13659     clearOnLoad : true,
13660
13661     /**
13662     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
13663     * property on loading, rather than expecting an array. (eg. more compatible to a standard
13664     * Grid query { data : [ .....] }
13665     */
13666     
13667     root : false,
13668      /**
13669     * @cfg {String} queryParam (optional) 
13670     * Name of the query as it will be passed on the querystring (defaults to 'node')
13671     * eg. the request will be ?node=[id]
13672     */
13673     
13674     
13675     queryParam: false,
13676     
13677     /**
13678      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
13679      * This is called automatically when a node is expanded, but may be used to reload
13680      * a node (or append new children if the {@link #clearOnLoad} option is false.)
13681      * @param {Roo.tree.TreeNode} node
13682      * @param {Function} callback
13683      */
13684     load : function(node, callback){
13685         if(this.clearOnLoad){
13686             while(node.firstChild){
13687                 node.removeChild(node.firstChild);
13688             }
13689         }
13690         if(node.attributes.children){ // preloaded json children
13691             var cs = node.attributes.children;
13692             for(var i = 0, len = cs.length; i < len; i++){
13693                 node.appendChild(this.createNode(cs[i]));
13694             }
13695             if(typeof callback == "function"){
13696                 callback();
13697             }
13698         }else if(this.dataUrl){
13699             this.requestData(node, callback);
13700         }
13701     },
13702
13703     getParams: function(node){
13704         var buf = [], bp = this.baseParams;
13705         for(var key in bp){
13706             if(typeof bp[key] != "function"){
13707                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
13708             }
13709         }
13710         var n = this.queryParam === false ? 'node' : this.queryParam;
13711         buf.push(n + "=", encodeURIComponent(node.id));
13712         return buf.join("");
13713     },
13714
13715     requestData : function(node, callback){
13716         if(this.fireEvent("beforeload", this, node, callback) !== false){
13717             this.transId = Roo.Ajax.request({
13718                 method:this.requestMethod,
13719                 url: this.dataUrl||this.url,
13720                 success: this.handleResponse,
13721                 failure: this.handleFailure,
13722                 scope: this,
13723                 argument: {callback: callback, node: node},
13724                 params: this.getParams(node)
13725             });
13726         }else{
13727             // if the load is cancelled, make sure we notify
13728             // the node that we are done
13729             if(typeof callback == "function"){
13730                 callback();
13731             }
13732         }
13733     },
13734
13735     isLoading : function(){
13736         return this.transId ? true : false;
13737     },
13738
13739     abort : function(){
13740         if(this.isLoading()){
13741             Roo.Ajax.abort(this.transId);
13742         }
13743     },
13744
13745     // private
13746     createNode : function(attr)
13747     {
13748         // apply baseAttrs, nice idea Corey!
13749         if(this.baseAttrs){
13750             Roo.applyIf(attr, this.baseAttrs);
13751         }
13752         if(this.applyLoader !== false){
13753             attr.loader = this;
13754         }
13755         // uiProvider = depreciated..
13756         
13757         if(typeof(attr.uiProvider) == 'string'){
13758            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
13759                 /**  eval:var:attr */ eval(attr.uiProvider);
13760         }
13761         if(typeof(this.uiProviders['default']) != 'undefined') {
13762             attr.uiProvider = this.uiProviders['default'];
13763         }
13764         
13765         this.fireEvent('create', this, attr);
13766         
13767         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
13768         return(attr.leaf ?
13769                         new Roo.tree.TreeNode(attr) :
13770                         new Roo.tree.AsyncTreeNode(attr));
13771     },
13772
13773     processResponse : function(response, node, callback)
13774     {
13775         var json = response.responseText;
13776         try {
13777             
13778             var o = Roo.decode(json);
13779             
13780             if (this.root === false && typeof(o.success) != undefined) {
13781                 this.root = 'data'; // the default behaviour for list like data..
13782                 }
13783                 
13784             if (this.root !== false &&  !o.success) {
13785                 // it's a failure condition.
13786                 var a = response.argument;
13787                 this.fireEvent("loadexception", this, a.node, response);
13788                 Roo.log("Load failed - should have a handler really");
13789                 return;
13790             }
13791             
13792             
13793             
13794             if (this.root !== false) {
13795                  o = o[this.root];
13796             }
13797             
13798             for(var i = 0, len = o.length; i < len; i++){
13799                 var n = this.createNode(o[i]);
13800                 if(n){
13801                     node.appendChild(n);
13802                 }
13803             }
13804             if(typeof callback == "function"){
13805                 callback(this, node);
13806             }
13807         }catch(e){
13808             this.handleFailure(response);
13809         }
13810     },
13811
13812     handleResponse : function(response){
13813         this.transId = false;
13814         var a = response.argument;
13815         this.processResponse(response, a.node, a.callback);
13816         this.fireEvent("load", this, a.node, response);
13817     },
13818
13819     handleFailure : function(response)
13820     {
13821         // should handle failure better..
13822         this.transId = false;
13823         var a = response.argument;
13824         this.fireEvent("loadexception", this, a.node, response);
13825         if(typeof a.callback == "function"){
13826             a.callback(this, a.node);
13827         }
13828     }
13829 });/*
13830  * Based on:
13831  * Ext JS Library 1.1.1
13832  * Copyright(c) 2006-2007, Ext JS, LLC.
13833  *
13834  * Originally Released Under LGPL - original licence link has changed is not relivant.
13835  *
13836  * Fork - LGPL
13837  * <script type="text/javascript">
13838  */
13839
13840 /**
13841 * @class Roo.tree.TreeFilter
13842 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
13843 * @param {TreePanel} tree
13844 * @param {Object} config (optional)
13845  */
13846 Roo.tree.TreeFilter = function(tree, config){
13847     this.tree = tree;
13848     this.filtered = {};
13849     Roo.apply(this, config);
13850 };
13851
13852 Roo.tree.TreeFilter.prototype = {
13853     clearBlank:false,
13854     reverse:false,
13855     autoClear:false,
13856     remove:false,
13857
13858      /**
13859      * Filter the data by a specific attribute.
13860      * @param {String/RegExp} value Either string that the attribute value
13861      * should start with or a RegExp to test against the attribute
13862      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
13863      * @param {TreeNode} startNode (optional) The node to start the filter at.
13864      */
13865     filter : function(value, attr, startNode){
13866         attr = attr || "text";
13867         var f;
13868         if(typeof value == "string"){
13869             var vlen = value.length;
13870             // auto clear empty filter
13871             if(vlen == 0 && this.clearBlank){
13872                 this.clear();
13873                 return;
13874             }
13875             value = value.toLowerCase();
13876             f = function(n){
13877                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
13878             };
13879         }else if(value.exec){ // regex?
13880             f = function(n){
13881                 return value.test(n.attributes[attr]);
13882             };
13883         }else{
13884             throw 'Illegal filter type, must be string or regex';
13885         }
13886         this.filterBy(f, null, startNode);
13887         },
13888
13889     /**
13890      * Filter by a function. The passed function will be called with each
13891      * node in the tree (or from the startNode). If the function returns true, the node is kept
13892      * otherwise it is filtered. If a node is filtered, its children are also filtered.
13893      * @param {Function} fn The filter function
13894      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
13895      */
13896     filterBy : function(fn, scope, startNode){
13897         startNode = startNode || this.tree.root;
13898         if(this.autoClear){
13899             this.clear();
13900         }
13901         var af = this.filtered, rv = this.reverse;
13902         var f = function(n){
13903             if(n == startNode){
13904                 return true;
13905             }
13906             if(af[n.id]){
13907                 return false;
13908             }
13909             var m = fn.call(scope || n, n);
13910             if(!m || rv){
13911                 af[n.id] = n;
13912                 n.ui.hide();
13913                 return false;
13914             }
13915             return true;
13916         };
13917         startNode.cascade(f);
13918         if(this.remove){
13919            for(var id in af){
13920                if(typeof id != "function"){
13921                    var n = af[id];
13922                    if(n && n.parentNode){
13923                        n.parentNode.removeChild(n);
13924                    }
13925                }
13926            }
13927         }
13928     },
13929
13930     /**
13931      * Clears the current filter. Note: with the "remove" option
13932      * set a filter cannot be cleared.
13933      */
13934     clear : function(){
13935         var t = this.tree;
13936         var af = this.filtered;
13937         for(var id in af){
13938             if(typeof id != "function"){
13939                 var n = af[id];
13940                 if(n){
13941                     n.ui.show();
13942                 }
13943             }
13944         }
13945         this.filtered = {};
13946     }
13947 };
13948 /*
13949  * Based on:
13950  * Ext JS Library 1.1.1
13951  * Copyright(c) 2006-2007, Ext JS, LLC.
13952  *
13953  * Originally Released Under LGPL - original licence link has changed is not relivant.
13954  *
13955  * Fork - LGPL
13956  * <script type="text/javascript">
13957  */
13958  
13959
13960 /**
13961  * @class Roo.tree.TreeSorter
13962  * Provides sorting of nodes in a TreePanel
13963  * 
13964  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
13965  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
13966  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
13967  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
13968  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
13969  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
13970  * @constructor
13971  * @param {TreePanel} tree
13972  * @param {Object} config
13973  */
13974 Roo.tree.TreeSorter = function(tree, config){
13975     Roo.apply(this, config);
13976     tree.on("beforechildrenrendered", this.doSort, this);
13977     tree.on("append", this.updateSort, this);
13978     tree.on("insert", this.updateSort, this);
13979     
13980     var dsc = this.dir && this.dir.toLowerCase() == "desc";
13981     var p = this.property || "text";
13982     var sortType = this.sortType;
13983     var fs = this.folderSort;
13984     var cs = this.caseSensitive === true;
13985     var leafAttr = this.leafAttr || 'leaf';
13986
13987     this.sortFn = function(n1, n2){
13988         if(fs){
13989             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
13990                 return 1;
13991             }
13992             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
13993                 return -1;
13994             }
13995         }
13996         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
13997         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
13998         if(v1 < v2){
13999                         return dsc ? +1 : -1;
14000                 }else if(v1 > v2){
14001                         return dsc ? -1 : +1;
14002         }else{
14003                 return 0;
14004         }
14005     };
14006 };
14007
14008 Roo.tree.TreeSorter.prototype = {
14009     doSort : function(node){
14010         node.sort(this.sortFn);
14011     },
14012     
14013     compareNodes : function(n1, n2){
14014         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
14015     },
14016     
14017     updateSort : function(tree, node){
14018         if(node.childrenRendered){
14019             this.doSort.defer(1, this, [node]);
14020         }
14021     }
14022 };/*
14023  * Based on:
14024  * Ext JS Library 1.1.1
14025  * Copyright(c) 2006-2007, Ext JS, LLC.
14026  *
14027  * Originally Released Under LGPL - original licence link has changed is not relivant.
14028  *
14029  * Fork - LGPL
14030  * <script type="text/javascript">
14031  */
14032
14033 if(Roo.dd.DropZone){
14034     
14035 Roo.tree.TreeDropZone = function(tree, config){
14036     this.allowParentInsert = false;
14037     this.allowContainerDrop = false;
14038     this.appendOnly = false;
14039     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
14040     this.tree = tree;
14041     this.lastInsertClass = "x-tree-no-status";
14042     this.dragOverData = {};
14043 };
14044
14045 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
14046     ddGroup : "TreeDD",
14047     scroll:  true,
14048     
14049     expandDelay : 1000,
14050     
14051     expandNode : function(node){
14052         if(node.hasChildNodes() && !node.isExpanded()){
14053             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
14054         }
14055     },
14056     
14057     queueExpand : function(node){
14058         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
14059     },
14060     
14061     cancelExpand : function(){
14062         if(this.expandProcId){
14063             clearTimeout(this.expandProcId);
14064             this.expandProcId = false;
14065         }
14066     },
14067     
14068     isValidDropPoint : function(n, pt, dd, e, data){
14069         if(!n || !data){ return false; }
14070         var targetNode = n.node;
14071         var dropNode = data.node;
14072         // default drop rules
14073         if(!(targetNode && targetNode.isTarget && pt)){
14074             return false;
14075         }
14076         if(pt == "append" && targetNode.allowChildren === false){
14077             return false;
14078         }
14079         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
14080             return false;
14081         }
14082         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
14083             return false;
14084         }
14085         // reuse the object
14086         var overEvent = this.dragOverData;
14087         overEvent.tree = this.tree;
14088         overEvent.target = targetNode;
14089         overEvent.data = data;
14090         overEvent.point = pt;
14091         overEvent.source = dd;
14092         overEvent.rawEvent = e;
14093         overEvent.dropNode = dropNode;
14094         overEvent.cancel = false;  
14095         var result = this.tree.fireEvent("nodedragover", overEvent);
14096         return overEvent.cancel === false && result !== false;
14097     },
14098     
14099     getDropPoint : function(e, n, dd)
14100     {
14101         var tn = n.node;
14102         if(tn.isRoot){
14103             return tn.allowChildren !== false ? "append" : false; // always append for root
14104         }
14105         var dragEl = n.ddel;
14106         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
14107         var y = Roo.lib.Event.getPageY(e);
14108         //var noAppend = tn.allowChildren === false || tn.isLeaf();
14109         
14110         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
14111         var noAppend = tn.allowChildren === false;
14112         if(this.appendOnly || tn.parentNode.allowChildren === false){
14113             return noAppend ? false : "append";
14114         }
14115         var noBelow = false;
14116         if(!this.allowParentInsert){
14117             noBelow = tn.hasChildNodes() && tn.isExpanded();
14118         }
14119         var q = (b - t) / (noAppend ? 2 : 3);
14120         if(y >= t && y < (t + q)){
14121             return "above";
14122         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
14123             return "below";
14124         }else{
14125             return "append";
14126         }
14127     },
14128     
14129     onNodeEnter : function(n, dd, e, data)
14130     {
14131         this.cancelExpand();
14132     },
14133     
14134     onNodeOver : function(n, dd, e, data)
14135     {
14136        
14137         var pt = this.getDropPoint(e, n, dd);
14138         var node = n.node;
14139         
14140         // auto node expand check
14141         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
14142             this.queueExpand(node);
14143         }else if(pt != "append"){
14144             this.cancelExpand();
14145         }
14146         
14147         // set the insert point style on the target node
14148         var returnCls = this.dropNotAllowed;
14149         if(this.isValidDropPoint(n, pt, dd, e, data)){
14150            if(pt){
14151                var el = n.ddel;
14152                var cls;
14153                if(pt == "above"){
14154                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
14155                    cls = "x-tree-drag-insert-above";
14156                }else if(pt == "below"){
14157                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
14158                    cls = "x-tree-drag-insert-below";
14159                }else{
14160                    returnCls = "x-tree-drop-ok-append";
14161                    cls = "x-tree-drag-append";
14162                }
14163                if(this.lastInsertClass != cls){
14164                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
14165                    this.lastInsertClass = cls;
14166                }
14167            }
14168        }
14169        return returnCls;
14170     },
14171     
14172     onNodeOut : function(n, dd, e, data){
14173         
14174         this.cancelExpand();
14175         this.removeDropIndicators(n);
14176     },
14177     
14178     onNodeDrop : function(n, dd, e, data){
14179         var point = this.getDropPoint(e, n, dd);
14180         var targetNode = n.node;
14181         targetNode.ui.startDrop();
14182         if(!this.isValidDropPoint(n, point, dd, e, data)){
14183             targetNode.ui.endDrop();
14184             return false;
14185         }
14186         // first try to find the drop node
14187         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
14188         var dropEvent = {
14189             tree : this.tree,
14190             target: targetNode,
14191             data: data,
14192             point: point,
14193             source: dd,
14194             rawEvent: e,
14195             dropNode: dropNode,
14196             cancel: !dropNode   
14197         };
14198         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
14199         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
14200             targetNode.ui.endDrop();
14201             return false;
14202         }
14203         // allow target changing
14204         targetNode = dropEvent.target;
14205         if(point == "append" && !targetNode.isExpanded()){
14206             targetNode.expand(false, null, function(){
14207                 this.completeDrop(dropEvent);
14208             }.createDelegate(this));
14209         }else{
14210             this.completeDrop(dropEvent);
14211         }
14212         return true;
14213     },
14214     
14215     completeDrop : function(de){
14216         var ns = de.dropNode, p = de.point, t = de.target;
14217         if(!(ns instanceof Array)){
14218             ns = [ns];
14219         }
14220         var n;
14221         for(var i = 0, len = ns.length; i < len; i++){
14222             n = ns[i];
14223             if(p == "above"){
14224                 t.parentNode.insertBefore(n, t);
14225             }else if(p == "below"){
14226                 t.parentNode.insertBefore(n, t.nextSibling);
14227             }else{
14228                 t.appendChild(n);
14229             }
14230         }
14231         n.ui.focus();
14232         if(this.tree.hlDrop){
14233             n.ui.highlight();
14234         }
14235         t.ui.endDrop();
14236         this.tree.fireEvent("nodedrop", de);
14237     },
14238     
14239     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
14240         if(this.tree.hlDrop){
14241             dropNode.ui.focus();
14242             dropNode.ui.highlight();
14243         }
14244         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
14245     },
14246     
14247     getTree : function(){
14248         return this.tree;
14249     },
14250     
14251     removeDropIndicators : function(n){
14252         if(n && n.ddel){
14253             var el = n.ddel;
14254             Roo.fly(el).removeClass([
14255                     "x-tree-drag-insert-above",
14256                     "x-tree-drag-insert-below",
14257                     "x-tree-drag-append"]);
14258             this.lastInsertClass = "_noclass";
14259         }
14260     },
14261     
14262     beforeDragDrop : function(target, e, id){
14263         this.cancelExpand();
14264         return true;
14265     },
14266     
14267     afterRepair : function(data){
14268         if(data && Roo.enableFx){
14269             data.node.ui.highlight();
14270         }
14271         this.hideProxy();
14272     } 
14273     
14274 });
14275
14276 }
14277 /*
14278  * Based on:
14279  * Ext JS Library 1.1.1
14280  * Copyright(c) 2006-2007, Ext JS, LLC.
14281  *
14282  * Originally Released Under LGPL - original licence link has changed is not relivant.
14283  *
14284  * Fork - LGPL
14285  * <script type="text/javascript">
14286  */
14287  
14288
14289 if(Roo.dd.DragZone){
14290 Roo.tree.TreeDragZone = function(tree, config){
14291     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
14292     this.tree = tree;
14293 };
14294
14295 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
14296     ddGroup : "TreeDD",
14297    
14298     onBeforeDrag : function(data, e){
14299         var n = data.node;
14300         return n && n.draggable && !n.disabled;
14301     },
14302      
14303     
14304     onInitDrag : function(e){
14305         var data = this.dragData;
14306         this.tree.getSelectionModel().select(data.node);
14307         this.proxy.update("");
14308         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
14309         this.tree.fireEvent("startdrag", this.tree, data.node, e);
14310     },
14311     
14312     getRepairXY : function(e, data){
14313         return data.node.ui.getDDRepairXY();
14314     },
14315     
14316     onEndDrag : function(data, e){
14317         this.tree.fireEvent("enddrag", this.tree, data.node, e);
14318         
14319         
14320     },
14321     
14322     onValidDrop : function(dd, e, id){
14323         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
14324         this.hideProxy();
14325     },
14326     
14327     beforeInvalidDrop : function(e, id){
14328         // this scrolls the original position back into view
14329         var sm = this.tree.getSelectionModel();
14330         sm.clearSelections();
14331         sm.select(this.dragData.node);
14332     }
14333 });
14334 }/*
14335  * Based on:
14336  * Ext JS Library 1.1.1
14337  * Copyright(c) 2006-2007, Ext JS, LLC.
14338  *
14339  * Originally Released Under LGPL - original licence link has changed is not relivant.
14340  *
14341  * Fork - LGPL
14342  * <script type="text/javascript">
14343  */
14344 /**
14345  * @class Roo.tree.TreeEditor
14346  * @extends Roo.Editor
14347  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
14348  * as the editor field.
14349  * @constructor
14350  * @param {Object} config (used to be the tree panel.)
14351  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
14352  * 
14353  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
14354  * @cfg {Roo.form.TextField|Object} field The field configuration
14355  *
14356  * 
14357  */
14358 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
14359     var tree = config;
14360     var field;
14361     if (oldconfig) { // old style..
14362         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
14363     } else {
14364         // new style..
14365         tree = config.tree;
14366         config.field = config.field  || {};
14367         config.field.xtype = 'TextField';
14368         field = Roo.factory(config.field, Roo.form);
14369     }
14370     config = config || {};
14371     
14372     
14373     this.addEvents({
14374         /**
14375          * @event beforenodeedit
14376          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
14377          * false from the handler of this event.
14378          * @param {Editor} this
14379          * @param {Roo.tree.Node} node 
14380          */
14381         "beforenodeedit" : true
14382     });
14383     
14384     //Roo.log(config);
14385     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
14386
14387     this.tree = tree;
14388
14389     tree.on('beforeclick', this.beforeNodeClick, this);
14390     tree.getTreeEl().on('mousedown', this.hide, this);
14391     this.on('complete', this.updateNode, this);
14392     this.on('beforestartedit', this.fitToTree, this);
14393     this.on('startedit', this.bindScroll, this, {delay:10});
14394     this.on('specialkey', this.onSpecialKey, this);
14395 };
14396
14397 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
14398     /**
14399      * @cfg {String} alignment
14400      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
14401      */
14402     alignment: "l-l",
14403     // inherit
14404     autoSize: false,
14405     /**
14406      * @cfg {Boolean} hideEl
14407      * True to hide the bound element while the editor is displayed (defaults to false)
14408      */
14409     hideEl : false,
14410     /**
14411      * @cfg {String} cls
14412      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
14413      */
14414     cls: "x-small-editor x-tree-editor",
14415     /**
14416      * @cfg {Boolean} shim
14417      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
14418      */
14419     shim:false,
14420     // inherit
14421     shadow:"frame",
14422     /**
14423      * @cfg {Number} maxWidth
14424      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
14425      * the containing tree element's size, it will be automatically limited for you to the container width, taking
14426      * scroll and client offsets into account prior to each edit.
14427      */
14428     maxWidth: 250,
14429
14430     editDelay : 350,
14431
14432     // private
14433     fitToTree : function(ed, el){
14434         var td = this.tree.getTreeEl().dom, nd = el.dom;
14435         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
14436             td.scrollLeft = nd.offsetLeft;
14437         }
14438         var w = Math.min(
14439                 this.maxWidth,
14440                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
14441         this.setSize(w, '');
14442         
14443         return this.fireEvent('beforenodeedit', this, this.editNode);
14444         
14445     },
14446
14447     // private
14448     triggerEdit : function(node){
14449         this.completeEdit();
14450         this.editNode = node;
14451         this.startEdit(node.ui.textNode, node.text);
14452     },
14453
14454     // private
14455     bindScroll : function(){
14456         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
14457     },
14458
14459     // private
14460     beforeNodeClick : function(node, e){
14461         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
14462         this.lastClick = new Date();
14463         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
14464             e.stopEvent();
14465             this.triggerEdit(node);
14466             return false;
14467         }
14468         return true;
14469     },
14470
14471     // private
14472     updateNode : function(ed, value){
14473         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
14474         this.editNode.setText(value);
14475     },
14476
14477     // private
14478     onHide : function(){
14479         Roo.tree.TreeEditor.superclass.onHide.call(this);
14480         if(this.editNode){
14481             this.editNode.ui.focus();
14482         }
14483     },
14484
14485     // private
14486     onSpecialKey : function(field, e){
14487         var k = e.getKey();
14488         if(k == e.ESC){
14489             e.stopEvent();
14490             this.cancelEdit();
14491         }else if(k == e.ENTER && !e.hasModifier()){
14492             e.stopEvent();
14493             this.completeEdit();
14494         }
14495     }
14496 });//<Script type="text/javascript">
14497 /*
14498  * Based on:
14499  * Ext JS Library 1.1.1
14500  * Copyright(c) 2006-2007, Ext JS, LLC.
14501  *
14502  * Originally Released Under LGPL - original licence link has changed is not relivant.
14503  *
14504  * Fork - LGPL
14505  * <script type="text/javascript">
14506  */
14507  
14508 /**
14509  * Not documented??? - probably should be...
14510  */
14511
14512 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
14513     //focus: Roo.emptyFn, // prevent odd scrolling behavior
14514     
14515     renderElements : function(n, a, targetNode, bulkRender){
14516         //consel.log("renderElements?");
14517         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
14518
14519         var t = n.getOwnerTree();
14520         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
14521         
14522         var cols = t.columns;
14523         var bw = t.borderWidth;
14524         var c = cols[0];
14525         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
14526          var cb = typeof a.checked == "boolean";
14527         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14528         var colcls = 'x-t-' + tid + '-c0';
14529         var buf = [
14530             '<li class="x-tree-node">',
14531             
14532                 
14533                 '<div class="x-tree-node-el ', a.cls,'">',
14534                     // extran...
14535                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
14536                 
14537                 
14538                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
14539                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
14540                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
14541                            (a.icon ? ' x-tree-node-inline-icon' : ''),
14542                            (a.iconCls ? ' '+a.iconCls : ''),
14543                            '" unselectable="on" />',
14544                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
14545                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
14546                              
14547                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14548                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
14549                             '<span unselectable="on" qtip="' + tx + '">',
14550                              tx,
14551                              '</span></a>' ,
14552                     '</div>',
14553                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14554                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
14555                  ];
14556         for(var i = 1, len = cols.length; i < len; i++){
14557             c = cols[i];
14558             colcls = 'x-t-' + tid + '-c' +i;
14559             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14560             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
14561                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
14562                       "</div>");
14563          }
14564          
14565          buf.push(
14566             '</a>',
14567             '<div class="x-clear"></div></div>',
14568             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
14569             "</li>");
14570         
14571         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
14572             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
14573                                 n.nextSibling.ui.getEl(), buf.join(""));
14574         }else{
14575             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
14576         }
14577         var el = this.wrap.firstChild;
14578         this.elRow = el;
14579         this.elNode = el.firstChild;
14580         this.ranchor = el.childNodes[1];
14581         this.ctNode = this.wrap.childNodes[1];
14582         var cs = el.firstChild.childNodes;
14583         this.indentNode = cs[0];
14584         this.ecNode = cs[1];
14585         this.iconNode = cs[2];
14586         var index = 3;
14587         if(cb){
14588             this.checkbox = cs[3];
14589             index++;
14590         }
14591         this.anchor = cs[index];
14592         
14593         this.textNode = cs[index].firstChild;
14594         
14595         //el.on("click", this.onClick, this);
14596         //el.on("dblclick", this.onDblClick, this);
14597         
14598         
14599        // console.log(this);
14600     },
14601     initEvents : function(){
14602         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
14603         
14604             
14605         var a = this.ranchor;
14606
14607         var el = Roo.get(a);
14608
14609         if(Roo.isOpera){ // opera render bug ignores the CSS
14610             el.setStyle("text-decoration", "none");
14611         }
14612
14613         el.on("click", this.onClick, this);
14614         el.on("dblclick", this.onDblClick, this);
14615         el.on("contextmenu", this.onContextMenu, this);
14616         
14617     },
14618     
14619     /*onSelectedChange : function(state){
14620         if(state){
14621             this.focus();
14622             this.addClass("x-tree-selected");
14623         }else{
14624             //this.blur();
14625             this.removeClass("x-tree-selected");
14626         }
14627     },*/
14628     addClass : function(cls){
14629         if(this.elRow){
14630             Roo.fly(this.elRow).addClass(cls);
14631         }
14632         
14633     },
14634     
14635     
14636     removeClass : function(cls){
14637         if(this.elRow){
14638             Roo.fly(this.elRow).removeClass(cls);
14639         }
14640     }
14641
14642     
14643     
14644 });//<Script type="text/javascript">
14645
14646 /*
14647  * Based on:
14648  * Ext JS Library 1.1.1
14649  * Copyright(c) 2006-2007, Ext JS, LLC.
14650  *
14651  * Originally Released Under LGPL - original licence link has changed is not relivant.
14652  *
14653  * Fork - LGPL
14654  * <script type="text/javascript">
14655  */
14656  
14657
14658 /**
14659  * @class Roo.tree.ColumnTree
14660  * @extends Roo.data.TreePanel
14661  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
14662  * @cfg {int} borderWidth  compined right/left border allowance
14663  * @constructor
14664  * @param {String/HTMLElement/Element} el The container element
14665  * @param {Object} config
14666  */
14667 Roo.tree.ColumnTree =  function(el, config)
14668 {
14669    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
14670    this.addEvents({
14671         /**
14672         * @event resize
14673         * Fire this event on a container when it resizes
14674         * @param {int} w Width
14675         * @param {int} h Height
14676         */
14677        "resize" : true
14678     });
14679     this.on('resize', this.onResize, this);
14680 };
14681
14682 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
14683     //lines:false,
14684     
14685     
14686     borderWidth: Roo.isBorderBox ? 0 : 2, 
14687     headEls : false,
14688     
14689     render : function(){
14690         // add the header.....
14691        
14692         Roo.tree.ColumnTree.superclass.render.apply(this);
14693         
14694         this.el.addClass('x-column-tree');
14695         
14696         this.headers = this.el.createChild(
14697             {cls:'x-tree-headers'},this.innerCt.dom);
14698    
14699         var cols = this.columns, c;
14700         var totalWidth = 0;
14701         this.headEls = [];
14702         var  len = cols.length;
14703         for(var i = 0; i < len; i++){
14704              c = cols[i];
14705              totalWidth += c.width;
14706             this.headEls.push(this.headers.createChild({
14707                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
14708                  cn: {
14709                      cls:'x-tree-hd-text',
14710                      html: c.header
14711                  },
14712                  style:'width:'+(c.width-this.borderWidth)+'px;'
14713              }));
14714         }
14715         this.headers.createChild({cls:'x-clear'});
14716         // prevent floats from wrapping when clipped
14717         this.headers.setWidth(totalWidth);
14718         //this.innerCt.setWidth(totalWidth);
14719         this.innerCt.setStyle({ overflow: 'auto' });
14720         this.onResize(this.width, this.height);
14721              
14722         
14723     },
14724     onResize : function(w,h)
14725     {
14726         this.height = h;
14727         this.width = w;
14728         // resize cols..
14729         this.innerCt.setWidth(this.width);
14730         this.innerCt.setHeight(this.height-20);
14731         
14732         // headers...
14733         var cols = this.columns, c;
14734         var totalWidth = 0;
14735         var expEl = false;
14736         var len = cols.length;
14737         for(var i = 0; i < len; i++){
14738             c = cols[i];
14739             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
14740                 // it's the expander..
14741                 expEl  = this.headEls[i];
14742                 continue;
14743             }
14744             totalWidth += c.width;
14745             
14746         }
14747         if (expEl) {
14748             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
14749         }
14750         this.headers.setWidth(w-20);
14751
14752         
14753         
14754         
14755     }
14756 });
14757 /*
14758  * Based on:
14759  * Ext JS Library 1.1.1
14760  * Copyright(c) 2006-2007, Ext JS, LLC.
14761  *
14762  * Originally Released Under LGPL - original licence link has changed is not relivant.
14763  *
14764  * Fork - LGPL
14765  * <script type="text/javascript">
14766  */
14767  
14768 /**
14769  * @class Roo.menu.Menu
14770  * @extends Roo.util.Observable
14771  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
14772  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
14773  * @constructor
14774  * Creates a new Menu
14775  * @param {Object} config Configuration options
14776  */
14777 Roo.menu.Menu = function(config){
14778     Roo.apply(this, config);
14779     this.id = this.id || Roo.id();
14780     this.addEvents({
14781         /**
14782          * @event beforeshow
14783          * Fires before this menu is displayed
14784          * @param {Roo.menu.Menu} this
14785          */
14786         beforeshow : true,
14787         /**
14788          * @event beforehide
14789          * Fires before this menu is hidden
14790          * @param {Roo.menu.Menu} this
14791          */
14792         beforehide : true,
14793         /**
14794          * @event show
14795          * Fires after this menu is displayed
14796          * @param {Roo.menu.Menu} this
14797          */
14798         show : true,
14799         /**
14800          * @event hide
14801          * Fires after this menu is hidden
14802          * @param {Roo.menu.Menu} this
14803          */
14804         hide : true,
14805         /**
14806          * @event click
14807          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
14808          * @param {Roo.menu.Menu} this
14809          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14810          * @param {Roo.EventObject} e
14811          */
14812         click : true,
14813         /**
14814          * @event mouseover
14815          * Fires when the mouse is hovering over this menu
14816          * @param {Roo.menu.Menu} this
14817          * @param {Roo.EventObject} e
14818          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14819          */
14820         mouseover : true,
14821         /**
14822          * @event mouseout
14823          * Fires when the mouse exits this menu
14824          * @param {Roo.menu.Menu} this
14825          * @param {Roo.EventObject} e
14826          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14827          */
14828         mouseout : true,
14829         /**
14830          * @event itemclick
14831          * Fires when a menu item contained in this menu is clicked
14832          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
14833          * @param {Roo.EventObject} e
14834          */
14835         itemclick: true
14836     });
14837     if (this.registerMenu) {
14838         Roo.menu.MenuMgr.register(this);
14839     }
14840     
14841     var mis = this.items;
14842     this.items = new Roo.util.MixedCollection();
14843     if(mis){
14844         this.add.apply(this, mis);
14845     }
14846 };
14847
14848 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
14849     /**
14850      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
14851      */
14852     minWidth : 120,
14853     /**
14854      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
14855      * for bottom-right shadow (defaults to "sides")
14856      */
14857     shadow : "sides",
14858     /**
14859      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
14860      * this menu (defaults to "tl-tr?")
14861      */
14862     subMenuAlign : "tl-tr?",
14863     /**
14864      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
14865      * relative to its element of origin (defaults to "tl-bl?")
14866      */
14867     defaultAlign : "tl-bl?",
14868     /**
14869      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
14870      */
14871     allowOtherMenus : false,
14872     /**
14873      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
14874      */
14875     registerMenu : true,
14876
14877     hidden:true,
14878
14879     // private
14880     render : function(){
14881         if(this.el){
14882             return;
14883         }
14884         var el = this.el = new Roo.Layer({
14885             cls: "x-menu",
14886             shadow:this.shadow,
14887             constrain: false,
14888             parentEl: this.parentEl || document.body,
14889             zindex:15000
14890         });
14891
14892         this.keyNav = new Roo.menu.MenuNav(this);
14893
14894         if(this.plain){
14895             el.addClass("x-menu-plain");
14896         }
14897         if(this.cls){
14898             el.addClass(this.cls);
14899         }
14900         // generic focus element
14901         this.focusEl = el.createChild({
14902             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
14903         });
14904         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
14905         //disabling touch- as it's causing issues ..
14906         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
14907         ul.on('click'   , this.onClick, this);
14908         
14909         
14910         ul.on("mouseover", this.onMouseOver, this);
14911         ul.on("mouseout", this.onMouseOut, this);
14912         this.items.each(function(item){
14913             if (item.hidden) {
14914                 return;
14915             }
14916             
14917             var li = document.createElement("li");
14918             li.className = "x-menu-list-item";
14919             ul.dom.appendChild(li);
14920             item.render(li, this);
14921         }, this);
14922         this.ul = ul;
14923         this.autoWidth();
14924     },
14925
14926     // private
14927     autoWidth : function(){
14928         var el = this.el, ul = this.ul;
14929         if(!el){
14930             return;
14931         }
14932         var w = this.width;
14933         if(w){
14934             el.setWidth(w);
14935         }else if(Roo.isIE){
14936             el.setWidth(this.minWidth);
14937             var t = el.dom.offsetWidth; // force recalc
14938             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
14939         }
14940     },
14941
14942     // private
14943     delayAutoWidth : function(){
14944         if(this.rendered){
14945             if(!this.awTask){
14946                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
14947             }
14948             this.awTask.delay(20);
14949         }
14950     },
14951
14952     // private
14953     findTargetItem : function(e){
14954         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
14955         if(t && t.menuItemId){
14956             return this.items.get(t.menuItemId);
14957         }
14958     },
14959
14960     // private
14961     onClick : function(e){
14962         Roo.log("menu.onClick");
14963         var t = this.findTargetItem(e);
14964         if(!t){
14965             return;
14966         }
14967         Roo.log(e);
14968         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
14969             if(t == this.activeItem && t.shouldDeactivate(e)){
14970                 this.activeItem.deactivate();
14971                 delete this.activeItem;
14972                 return;
14973             }
14974             if(t.canActivate){
14975                 this.setActiveItem(t, true);
14976             }
14977             return;
14978             
14979             
14980         }
14981         
14982         t.onClick(e);
14983         this.fireEvent("click", this, t, e);
14984     },
14985
14986     // private
14987     setActiveItem : function(item, autoExpand){
14988         if(item != this.activeItem){
14989             if(this.activeItem){
14990                 this.activeItem.deactivate();
14991             }
14992             this.activeItem = item;
14993             item.activate(autoExpand);
14994         }else if(autoExpand){
14995             item.expandMenu();
14996         }
14997     },
14998
14999     // private
15000     tryActivate : function(start, step){
15001         var items = this.items;
15002         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
15003             var item = items.get(i);
15004             if(!item.disabled && item.canActivate){
15005                 this.setActiveItem(item, false);
15006                 return item;
15007             }
15008         }
15009         return false;
15010     },
15011
15012     // private
15013     onMouseOver : function(e){
15014         var t;
15015         if(t = this.findTargetItem(e)){
15016             if(t.canActivate && !t.disabled){
15017                 this.setActiveItem(t, true);
15018             }
15019         }
15020         this.fireEvent("mouseover", this, e, t);
15021     },
15022
15023     // private
15024     onMouseOut : function(e){
15025         var t;
15026         if(t = this.findTargetItem(e)){
15027             if(t == this.activeItem && t.shouldDeactivate(e)){
15028                 this.activeItem.deactivate();
15029                 delete this.activeItem;
15030             }
15031         }
15032         this.fireEvent("mouseout", this, e, t);
15033     },
15034
15035     /**
15036      * Read-only.  Returns true if the menu is currently displayed, else false.
15037      * @type Boolean
15038      */
15039     isVisible : function(){
15040         return this.el && !this.hidden;
15041     },
15042
15043     /**
15044      * Displays this menu relative to another element
15045      * @param {String/HTMLElement/Roo.Element} element The element to align to
15046      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
15047      * the element (defaults to this.defaultAlign)
15048      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
15049      */
15050     show : function(el, pos, parentMenu){
15051         this.parentMenu = parentMenu;
15052         if(!this.el){
15053             this.render();
15054         }
15055         this.fireEvent("beforeshow", this);
15056         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
15057     },
15058
15059     /**
15060      * Displays this menu at a specific xy position
15061      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
15062      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
15063      */
15064     showAt : function(xy, parentMenu, /* private: */_e){
15065         this.parentMenu = parentMenu;
15066         if(!this.el){
15067             this.render();
15068         }
15069         if(_e !== false){
15070             this.fireEvent("beforeshow", this);
15071             xy = this.el.adjustForConstraints(xy);
15072         }
15073         this.el.setXY(xy);
15074         this.el.show();
15075         this.hidden = false;
15076         this.focus();
15077         this.fireEvent("show", this);
15078     },
15079
15080     focus : function(){
15081         if(!this.hidden){
15082             this.doFocus.defer(50, this);
15083         }
15084     },
15085
15086     doFocus : function(){
15087         if(!this.hidden){
15088             this.focusEl.focus();
15089         }
15090     },
15091
15092     /**
15093      * Hides this menu and optionally all parent menus
15094      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
15095      */
15096     hide : function(deep){
15097         if(this.el && this.isVisible()){
15098             this.fireEvent("beforehide", this);
15099             if(this.activeItem){
15100                 this.activeItem.deactivate();
15101                 this.activeItem = null;
15102             }
15103             this.el.hide();
15104             this.hidden = true;
15105             this.fireEvent("hide", this);
15106         }
15107         if(deep === true && this.parentMenu){
15108             this.parentMenu.hide(true);
15109         }
15110     },
15111
15112     /**
15113      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
15114      * Any of the following are valid:
15115      * <ul>
15116      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
15117      * <li>An HTMLElement object which will be converted to a menu item</li>
15118      * <li>A menu item config object that will be created as a new menu item</li>
15119      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
15120      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
15121      * </ul>
15122      * Usage:
15123      * <pre><code>
15124 // Create the menu
15125 var menu = new Roo.menu.Menu();
15126
15127 // Create a menu item to add by reference
15128 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
15129
15130 // Add a bunch of items at once using different methods.
15131 // Only the last item added will be returned.
15132 var item = menu.add(
15133     menuItem,                // add existing item by ref
15134     'Dynamic Item',          // new TextItem
15135     '-',                     // new separator
15136     { text: 'Config Item' }  // new item by config
15137 );
15138 </code></pre>
15139      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
15140      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
15141      */
15142     add : function(){
15143         var a = arguments, l = a.length, item;
15144         for(var i = 0; i < l; i++){
15145             var el = a[i];
15146             if ((typeof(el) == "object") && el.xtype && el.xns) {
15147                 el = Roo.factory(el, Roo.menu);
15148             }
15149             
15150             if(el.render){ // some kind of Item
15151                 item = this.addItem(el);
15152             }else if(typeof el == "string"){ // string
15153                 if(el == "separator" || el == "-"){
15154                     item = this.addSeparator();
15155                 }else{
15156                     item = this.addText(el);
15157                 }
15158             }else if(el.tagName || el.el){ // element
15159                 item = this.addElement(el);
15160             }else if(typeof el == "object"){ // must be menu item config?
15161                 item = this.addMenuItem(el);
15162             }
15163         }
15164         return item;
15165     },
15166
15167     /**
15168      * Returns this menu's underlying {@link Roo.Element} object
15169      * @return {Roo.Element} The element
15170      */
15171     getEl : function(){
15172         if(!this.el){
15173             this.render();
15174         }
15175         return this.el;
15176     },
15177
15178     /**
15179      * Adds a separator bar to the menu
15180      * @return {Roo.menu.Item} The menu item that was added
15181      */
15182     addSeparator : function(){
15183         return this.addItem(new Roo.menu.Separator());
15184     },
15185
15186     /**
15187      * Adds an {@link Roo.Element} object to the menu
15188      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
15189      * @return {Roo.menu.Item} The menu item that was added
15190      */
15191     addElement : function(el){
15192         return this.addItem(new Roo.menu.BaseItem(el));
15193     },
15194
15195     /**
15196      * Adds an existing object based on {@link Roo.menu.Item} to the menu
15197      * @param {Roo.menu.Item} item The menu item to add
15198      * @return {Roo.menu.Item} The menu item that was added
15199      */
15200     addItem : function(item){
15201         this.items.add(item);
15202         if(this.ul){
15203             var li = document.createElement("li");
15204             li.className = "x-menu-list-item";
15205             this.ul.dom.appendChild(li);
15206             item.render(li, this);
15207             this.delayAutoWidth();
15208         }
15209         return item;
15210     },
15211
15212     /**
15213      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
15214      * @param {Object} config A MenuItem config object
15215      * @return {Roo.menu.Item} The menu item that was added
15216      */
15217     addMenuItem : function(config){
15218         if(!(config instanceof Roo.menu.Item)){
15219             if(typeof config.checked == "boolean"){ // must be check menu item config?
15220                 config = new Roo.menu.CheckItem(config);
15221             }else{
15222                 config = new Roo.menu.Item(config);
15223             }
15224         }
15225         return this.addItem(config);
15226     },
15227
15228     /**
15229      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
15230      * @param {String} text The text to display in the menu item
15231      * @return {Roo.menu.Item} The menu item that was added
15232      */
15233     addText : function(text){
15234         return this.addItem(new Roo.menu.TextItem({ text : text }));
15235     },
15236
15237     /**
15238      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
15239      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
15240      * @param {Roo.menu.Item} item The menu item to add
15241      * @return {Roo.menu.Item} The menu item that was added
15242      */
15243     insert : function(index, item){
15244         this.items.insert(index, item);
15245         if(this.ul){
15246             var li = document.createElement("li");
15247             li.className = "x-menu-list-item";
15248             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
15249             item.render(li, this);
15250             this.delayAutoWidth();
15251         }
15252         return item;
15253     },
15254
15255     /**
15256      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
15257      * @param {Roo.menu.Item} item The menu item to remove
15258      */
15259     remove : function(item){
15260         this.items.removeKey(item.id);
15261         item.destroy();
15262     },
15263
15264     /**
15265      * Removes and destroys all items in the menu
15266      */
15267     removeAll : function(){
15268         var f;
15269         while(f = this.items.first()){
15270             this.remove(f);
15271         }
15272     }
15273 });
15274
15275 // MenuNav is a private utility class used internally by the Menu
15276 Roo.menu.MenuNav = function(menu){
15277     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
15278     this.scope = this.menu = menu;
15279 };
15280
15281 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
15282     doRelay : function(e, h){
15283         var k = e.getKey();
15284         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
15285             this.menu.tryActivate(0, 1);
15286             return false;
15287         }
15288         return h.call(this.scope || this, e, this.menu);
15289     },
15290
15291     up : function(e, m){
15292         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
15293             m.tryActivate(m.items.length-1, -1);
15294         }
15295     },
15296
15297     down : function(e, m){
15298         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
15299             m.tryActivate(0, 1);
15300         }
15301     },
15302
15303     right : function(e, m){
15304         if(m.activeItem){
15305             m.activeItem.expandMenu(true);
15306         }
15307     },
15308
15309     left : function(e, m){
15310         m.hide();
15311         if(m.parentMenu && m.parentMenu.activeItem){
15312             m.parentMenu.activeItem.activate();
15313         }
15314     },
15315
15316     enter : function(e, m){
15317         if(m.activeItem){
15318             e.stopPropagation();
15319             m.activeItem.onClick(e);
15320             m.fireEvent("click", this, m.activeItem);
15321             return true;
15322         }
15323     }
15324 });/*
15325  * Based on:
15326  * Ext JS Library 1.1.1
15327  * Copyright(c) 2006-2007, Ext JS, LLC.
15328  *
15329  * Originally Released Under LGPL - original licence link has changed is not relivant.
15330  *
15331  * Fork - LGPL
15332  * <script type="text/javascript">
15333  */
15334  
15335 /**
15336  * @class Roo.menu.MenuMgr
15337  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
15338  * @singleton
15339  */
15340 Roo.menu.MenuMgr = function(){
15341    var menus, active, groups = {}, attached = false, lastShow = new Date();
15342
15343    // private - called when first menu is created
15344    function init(){
15345        menus = {};
15346        active = new Roo.util.MixedCollection();
15347        Roo.get(document).addKeyListener(27, function(){
15348            if(active.length > 0){
15349                hideAll();
15350            }
15351        });
15352    }
15353
15354    // private
15355    function hideAll(){
15356        if(active && active.length > 0){
15357            var c = active.clone();
15358            c.each(function(m){
15359                m.hide();
15360            });
15361        }
15362    }
15363
15364    // private
15365    function onHide(m){
15366        active.remove(m);
15367        if(active.length < 1){
15368            Roo.get(document).un("mousedown", onMouseDown);
15369            attached = false;
15370        }
15371    }
15372
15373    // private
15374    function onShow(m){
15375        var last = active.last();
15376        lastShow = new Date();
15377        active.add(m);
15378        if(!attached){
15379            Roo.get(document).on("mousedown", onMouseDown);
15380            attached = true;
15381        }
15382        if(m.parentMenu){
15383           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
15384           m.parentMenu.activeChild = m;
15385        }else if(last && last.isVisible()){
15386           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
15387        }
15388    }
15389
15390    // private
15391    function onBeforeHide(m){
15392        if(m.activeChild){
15393            m.activeChild.hide();
15394        }
15395        if(m.autoHideTimer){
15396            clearTimeout(m.autoHideTimer);
15397            delete m.autoHideTimer;
15398        }
15399    }
15400
15401    // private
15402    function onBeforeShow(m){
15403        var pm = m.parentMenu;
15404        if(!pm && !m.allowOtherMenus){
15405            hideAll();
15406        }else if(pm && pm.activeChild && active != m){
15407            pm.activeChild.hide();
15408        }
15409    }
15410
15411    // private
15412    function onMouseDown(e){
15413        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
15414            hideAll();
15415        }
15416    }
15417
15418    // private
15419    function onBeforeCheck(mi, state){
15420        if(state){
15421            var g = groups[mi.group];
15422            for(var i = 0, l = g.length; i < l; i++){
15423                if(g[i] != mi){
15424                    g[i].setChecked(false);
15425                }
15426            }
15427        }
15428    }
15429
15430    return {
15431
15432        /**
15433         * Hides all menus that are currently visible
15434         */
15435        hideAll : function(){
15436             hideAll();  
15437        },
15438
15439        // private
15440        register : function(menu){
15441            if(!menus){
15442                init();
15443            }
15444            menus[menu.id] = menu;
15445            menu.on("beforehide", onBeforeHide);
15446            menu.on("hide", onHide);
15447            menu.on("beforeshow", onBeforeShow);
15448            menu.on("show", onShow);
15449            var g = menu.group;
15450            if(g && menu.events["checkchange"]){
15451                if(!groups[g]){
15452                    groups[g] = [];
15453                }
15454                groups[g].push(menu);
15455                menu.on("checkchange", onCheck);
15456            }
15457        },
15458
15459         /**
15460          * Returns a {@link Roo.menu.Menu} object
15461          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
15462          * be used to generate and return a new Menu instance.
15463          */
15464        get : function(menu){
15465            if(typeof menu == "string"){ // menu id
15466                return menus[menu];
15467            }else if(menu.events){  // menu instance
15468                return menu;
15469            }else if(typeof menu.length == 'number'){ // array of menu items?
15470                return new Roo.menu.Menu({items:menu});
15471            }else{ // otherwise, must be a config
15472                return new Roo.menu.Menu(menu);
15473            }
15474        },
15475
15476        // private
15477        unregister : function(menu){
15478            delete menus[menu.id];
15479            menu.un("beforehide", onBeforeHide);
15480            menu.un("hide", onHide);
15481            menu.un("beforeshow", onBeforeShow);
15482            menu.un("show", onShow);
15483            var g = menu.group;
15484            if(g && menu.events["checkchange"]){
15485                groups[g].remove(menu);
15486                menu.un("checkchange", onCheck);
15487            }
15488        },
15489
15490        // private
15491        registerCheckable : function(menuItem){
15492            var g = menuItem.group;
15493            if(g){
15494                if(!groups[g]){
15495                    groups[g] = [];
15496                }
15497                groups[g].push(menuItem);
15498                menuItem.on("beforecheckchange", onBeforeCheck);
15499            }
15500        },
15501
15502        // private
15503        unregisterCheckable : function(menuItem){
15504            var g = menuItem.group;
15505            if(g){
15506                groups[g].remove(menuItem);
15507                menuItem.un("beforecheckchange", onBeforeCheck);
15508            }
15509        }
15510    };
15511 }();/*
15512  * Based on:
15513  * Ext JS Library 1.1.1
15514  * Copyright(c) 2006-2007, Ext JS, LLC.
15515  *
15516  * Originally Released Under LGPL - original licence link has changed is not relivant.
15517  *
15518  * Fork - LGPL
15519  * <script type="text/javascript">
15520  */
15521  
15522
15523 /**
15524  * @class Roo.menu.BaseItem
15525  * @extends Roo.Component
15526  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
15527  * management and base configuration options shared by all menu components.
15528  * @constructor
15529  * Creates a new BaseItem
15530  * @param {Object} config Configuration options
15531  */
15532 Roo.menu.BaseItem = function(config){
15533     Roo.menu.BaseItem.superclass.constructor.call(this, config);
15534
15535     this.addEvents({
15536         /**
15537          * @event click
15538          * Fires when this item is clicked
15539          * @param {Roo.menu.BaseItem} this
15540          * @param {Roo.EventObject} e
15541          */
15542         click: true,
15543         /**
15544          * @event activate
15545          * Fires when this item is activated
15546          * @param {Roo.menu.BaseItem} this
15547          */
15548         activate : true,
15549         /**
15550          * @event deactivate
15551          * Fires when this item is deactivated
15552          * @param {Roo.menu.BaseItem} this
15553          */
15554         deactivate : true
15555     });
15556
15557     if(this.handler){
15558         this.on("click", this.handler, this.scope, true);
15559     }
15560 };
15561
15562 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
15563     /**
15564      * @cfg {Function} handler
15565      * A function that will handle the click event of this menu item (defaults to undefined)
15566      */
15567     /**
15568      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
15569      */
15570     canActivate : false,
15571     
15572      /**
15573      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
15574      */
15575     hidden: false,
15576     
15577     /**
15578      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
15579      */
15580     activeClass : "x-menu-item-active",
15581     /**
15582      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
15583      */
15584     hideOnClick : true,
15585     /**
15586      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
15587      */
15588     hideDelay : 100,
15589
15590     // private
15591     ctype: "Roo.menu.BaseItem",
15592
15593     // private
15594     actionMode : "container",
15595
15596     // private
15597     render : function(container, parentMenu){
15598         this.parentMenu = parentMenu;
15599         Roo.menu.BaseItem.superclass.render.call(this, container);
15600         this.container.menuItemId = this.id;
15601     },
15602
15603     // private
15604     onRender : function(container, position){
15605         this.el = Roo.get(this.el);
15606         container.dom.appendChild(this.el.dom);
15607     },
15608
15609     // private
15610     onClick : function(e){
15611         if(!this.disabled && this.fireEvent("click", this, e) !== false
15612                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
15613             this.handleClick(e);
15614         }else{
15615             e.stopEvent();
15616         }
15617     },
15618
15619     // private
15620     activate : function(){
15621         if(this.disabled){
15622             return false;
15623         }
15624         var li = this.container;
15625         li.addClass(this.activeClass);
15626         this.region = li.getRegion().adjust(2, 2, -2, -2);
15627         this.fireEvent("activate", this);
15628         return true;
15629     },
15630
15631     // private
15632     deactivate : function(){
15633         this.container.removeClass(this.activeClass);
15634         this.fireEvent("deactivate", this);
15635     },
15636
15637     // private
15638     shouldDeactivate : function(e){
15639         return !this.region || !this.region.contains(e.getPoint());
15640     },
15641
15642     // private
15643     handleClick : function(e){
15644         if(this.hideOnClick){
15645             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
15646         }
15647     },
15648
15649     // private
15650     expandMenu : function(autoActivate){
15651         // do nothing
15652     },
15653
15654     // private
15655     hideMenu : function(){
15656         // do nothing
15657     }
15658 });/*
15659  * Based on:
15660  * Ext JS Library 1.1.1
15661  * Copyright(c) 2006-2007, Ext JS, LLC.
15662  *
15663  * Originally Released Under LGPL - original licence link has changed is not relivant.
15664  *
15665  * Fork - LGPL
15666  * <script type="text/javascript">
15667  */
15668  
15669 /**
15670  * @class Roo.menu.Adapter
15671  * @extends Roo.menu.BaseItem
15672  * 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.
15673  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
15674  * @constructor
15675  * Creates a new Adapter
15676  * @param {Object} config Configuration options
15677  */
15678 Roo.menu.Adapter = function(component, config){
15679     Roo.menu.Adapter.superclass.constructor.call(this, config);
15680     this.component = component;
15681 };
15682 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
15683     // private
15684     canActivate : true,
15685
15686     // private
15687     onRender : function(container, position){
15688         this.component.render(container);
15689         this.el = this.component.getEl();
15690     },
15691
15692     // private
15693     activate : function(){
15694         if(this.disabled){
15695             return false;
15696         }
15697         this.component.focus();
15698         this.fireEvent("activate", this);
15699         return true;
15700     },
15701
15702     // private
15703     deactivate : function(){
15704         this.fireEvent("deactivate", this);
15705     },
15706
15707     // private
15708     disable : function(){
15709         this.component.disable();
15710         Roo.menu.Adapter.superclass.disable.call(this);
15711     },
15712
15713     // private
15714     enable : function(){
15715         this.component.enable();
15716         Roo.menu.Adapter.superclass.enable.call(this);
15717     }
15718 });/*
15719  * Based on:
15720  * Ext JS Library 1.1.1
15721  * Copyright(c) 2006-2007, Ext JS, LLC.
15722  *
15723  * Originally Released Under LGPL - original licence link has changed is not relivant.
15724  *
15725  * Fork - LGPL
15726  * <script type="text/javascript">
15727  */
15728
15729 /**
15730  * @class Roo.menu.TextItem
15731  * @extends Roo.menu.BaseItem
15732  * Adds a static text string to a menu, usually used as either a heading or group separator.
15733  * Note: old style constructor with text is still supported.
15734  * 
15735  * @constructor
15736  * Creates a new TextItem
15737  * @param {Object} cfg Configuration
15738  */
15739 Roo.menu.TextItem = function(cfg){
15740     if (typeof(cfg) == 'string') {
15741         this.text = cfg;
15742     } else {
15743         Roo.apply(this,cfg);
15744     }
15745     
15746     Roo.menu.TextItem.superclass.constructor.call(this);
15747 };
15748
15749 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
15750     /**
15751      * @cfg {Boolean} text Text to show on item.
15752      */
15753     text : '',
15754     
15755     /**
15756      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15757      */
15758     hideOnClick : false,
15759     /**
15760      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
15761      */
15762     itemCls : "x-menu-text",
15763
15764     // private
15765     onRender : function(){
15766         var s = document.createElement("span");
15767         s.className = this.itemCls;
15768         s.innerHTML = this.text;
15769         this.el = s;
15770         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
15771     }
15772 });/*
15773  * Based on:
15774  * Ext JS Library 1.1.1
15775  * Copyright(c) 2006-2007, Ext JS, LLC.
15776  *
15777  * Originally Released Under LGPL - original licence link has changed is not relivant.
15778  *
15779  * Fork - LGPL
15780  * <script type="text/javascript">
15781  */
15782
15783 /**
15784  * @class Roo.menu.Separator
15785  * @extends Roo.menu.BaseItem
15786  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
15787  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
15788  * @constructor
15789  * @param {Object} config Configuration options
15790  */
15791 Roo.menu.Separator = function(config){
15792     Roo.menu.Separator.superclass.constructor.call(this, config);
15793 };
15794
15795 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
15796     /**
15797      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
15798      */
15799     itemCls : "x-menu-sep",
15800     /**
15801      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15802      */
15803     hideOnClick : false,
15804
15805     // private
15806     onRender : function(li){
15807         var s = document.createElement("span");
15808         s.className = this.itemCls;
15809         s.innerHTML = "&#160;";
15810         this.el = s;
15811         li.addClass("x-menu-sep-li");
15812         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
15813     }
15814 });/*
15815  * Based on:
15816  * Ext JS Library 1.1.1
15817  * Copyright(c) 2006-2007, Ext JS, LLC.
15818  *
15819  * Originally Released Under LGPL - original licence link has changed is not relivant.
15820  *
15821  * Fork - LGPL
15822  * <script type="text/javascript">
15823  */
15824 /**
15825  * @class Roo.menu.Item
15826  * @extends Roo.menu.BaseItem
15827  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
15828  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
15829  * activation and click handling.
15830  * @constructor
15831  * Creates a new Item
15832  * @param {Object} config Configuration options
15833  */
15834 Roo.menu.Item = function(config){
15835     Roo.menu.Item.superclass.constructor.call(this, config);
15836     if(this.menu){
15837         this.menu = Roo.menu.MenuMgr.get(this.menu);
15838     }
15839 };
15840 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
15841     
15842     /**
15843      * @cfg {String} text
15844      * The text to show on the menu item.
15845      */
15846     text: '',
15847      /**
15848      * @cfg {String} HTML to render in menu
15849      * The text to show on the menu item (HTML version).
15850      */
15851     html: '',
15852     /**
15853      * @cfg {String} icon
15854      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
15855      */
15856     icon: undefined,
15857     /**
15858      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
15859      */
15860     itemCls : "x-menu-item",
15861     /**
15862      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
15863      */
15864     canActivate : true,
15865     /**
15866      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
15867      */
15868     showDelay: 200,
15869     // doc'd in BaseItem
15870     hideDelay: 200,
15871
15872     // private
15873     ctype: "Roo.menu.Item",
15874     
15875     // private
15876     onRender : function(container, position){
15877         var el = document.createElement("a");
15878         el.hideFocus = true;
15879         el.unselectable = "on";
15880         el.href = this.href || "#";
15881         if(this.hrefTarget){
15882             el.target = this.hrefTarget;
15883         }
15884         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
15885         
15886         var html = this.html.length ? this.html  : String.format('{0}',this.text);
15887         
15888         el.innerHTML = String.format(
15889                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
15890                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
15891         this.el = el;
15892         Roo.menu.Item.superclass.onRender.call(this, container, position);
15893     },
15894
15895     /**
15896      * Sets the text to display in this menu item
15897      * @param {String} text The text to display
15898      * @param {Boolean} isHTML true to indicate text is pure html.
15899      */
15900     setText : function(text, isHTML){
15901         if (isHTML) {
15902             this.html = text;
15903         } else {
15904             this.text = text;
15905             this.html = '';
15906         }
15907         if(this.rendered){
15908             var html = this.html.length ? this.html  : String.format('{0}',this.text);
15909      
15910             this.el.update(String.format(
15911                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
15912                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
15913             this.parentMenu.autoWidth();
15914         }
15915     },
15916
15917     // private
15918     handleClick : function(e){
15919         if(!this.href){ // if no link defined, stop the event automatically
15920             e.stopEvent();
15921         }
15922         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
15923     },
15924
15925     // private
15926     activate : function(autoExpand){
15927         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
15928             this.focus();
15929             if(autoExpand){
15930                 this.expandMenu();
15931             }
15932         }
15933         return true;
15934     },
15935
15936     // private
15937     shouldDeactivate : function(e){
15938         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
15939             if(this.menu && this.menu.isVisible()){
15940                 return !this.menu.getEl().getRegion().contains(e.getPoint());
15941             }
15942             return true;
15943         }
15944         return false;
15945     },
15946
15947     // private
15948     deactivate : function(){
15949         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
15950         this.hideMenu();
15951     },
15952
15953     // private
15954     expandMenu : function(autoActivate){
15955         if(!this.disabled && this.menu){
15956             clearTimeout(this.hideTimer);
15957             delete this.hideTimer;
15958             if(!this.menu.isVisible() && !this.showTimer){
15959                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
15960             }else if (this.menu.isVisible() && autoActivate){
15961                 this.menu.tryActivate(0, 1);
15962             }
15963         }
15964     },
15965
15966     // private
15967     deferExpand : function(autoActivate){
15968         delete this.showTimer;
15969         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
15970         if(autoActivate){
15971             this.menu.tryActivate(0, 1);
15972         }
15973     },
15974
15975     // private
15976     hideMenu : function(){
15977         clearTimeout(this.showTimer);
15978         delete this.showTimer;
15979         if(!this.hideTimer && this.menu && this.menu.isVisible()){
15980             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
15981         }
15982     },
15983
15984     // private
15985     deferHide : function(){
15986         delete this.hideTimer;
15987         this.menu.hide();
15988     }
15989 });/*
15990  * Based on:
15991  * Ext JS Library 1.1.1
15992  * Copyright(c) 2006-2007, Ext JS, LLC.
15993  *
15994  * Originally Released Under LGPL - original licence link has changed is not relivant.
15995  *
15996  * Fork - LGPL
15997  * <script type="text/javascript">
15998  */
15999  
16000 /**
16001  * @class Roo.menu.CheckItem
16002  * @extends Roo.menu.Item
16003  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
16004  * @constructor
16005  * Creates a new CheckItem
16006  * @param {Object} config Configuration options
16007  */
16008 Roo.menu.CheckItem = function(config){
16009     Roo.menu.CheckItem.superclass.constructor.call(this, config);
16010     this.addEvents({
16011         /**
16012          * @event beforecheckchange
16013          * Fires before the checked value is set, providing an opportunity to cancel if needed
16014          * @param {Roo.menu.CheckItem} this
16015          * @param {Boolean} checked The new checked value that will be set
16016          */
16017         "beforecheckchange" : true,
16018         /**
16019          * @event checkchange
16020          * Fires after the checked value has been set
16021          * @param {Roo.menu.CheckItem} this
16022          * @param {Boolean} checked The checked value that was set
16023          */
16024         "checkchange" : true
16025     });
16026     if(this.checkHandler){
16027         this.on('checkchange', this.checkHandler, this.scope);
16028     }
16029 };
16030 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
16031     /**
16032      * @cfg {String} group
16033      * All check items with the same group name will automatically be grouped into a single-select
16034      * radio button group (defaults to '')
16035      */
16036     /**
16037      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
16038      */
16039     itemCls : "x-menu-item x-menu-check-item",
16040     /**
16041      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
16042      */
16043     groupClass : "x-menu-group-item",
16044
16045     /**
16046      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
16047      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
16048      * initialized with checked = true will be rendered as checked.
16049      */
16050     checked: false,
16051
16052     // private
16053     ctype: "Roo.menu.CheckItem",
16054
16055     // private
16056     onRender : function(c){
16057         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
16058         if(this.group){
16059             this.el.addClass(this.groupClass);
16060         }
16061         Roo.menu.MenuMgr.registerCheckable(this);
16062         if(this.checked){
16063             this.checked = false;
16064             this.setChecked(true, true);
16065         }
16066     },
16067
16068     // private
16069     destroy : function(){
16070         if(this.rendered){
16071             Roo.menu.MenuMgr.unregisterCheckable(this);
16072         }
16073         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
16074     },
16075
16076     /**
16077      * Set the checked state of this item
16078      * @param {Boolean} checked The new checked value
16079      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
16080      */
16081     setChecked : function(state, suppressEvent){
16082         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
16083             if(this.container){
16084                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
16085             }
16086             this.checked = state;
16087             if(suppressEvent !== true){
16088                 this.fireEvent("checkchange", this, state);
16089             }
16090         }
16091     },
16092
16093     // private
16094     handleClick : function(e){
16095        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
16096            this.setChecked(!this.checked);
16097        }
16098        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
16099     }
16100 });/*
16101  * Based on:
16102  * Ext JS Library 1.1.1
16103  * Copyright(c) 2006-2007, Ext JS, LLC.
16104  *
16105  * Originally Released Under LGPL - original licence link has changed is not relivant.
16106  *
16107  * Fork - LGPL
16108  * <script type="text/javascript">
16109  */
16110  
16111 /**
16112  * @class Roo.menu.DateItem
16113  * @extends Roo.menu.Adapter
16114  * A menu item that wraps the {@link Roo.DatPicker} component.
16115  * @constructor
16116  * Creates a new DateItem
16117  * @param {Object} config Configuration options
16118  */
16119 Roo.menu.DateItem = function(config){
16120     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
16121     /** The Roo.DatePicker object @type Roo.DatePicker */
16122     this.picker = this.component;
16123     this.addEvents({select: true});
16124     
16125     this.picker.on("render", function(picker){
16126         picker.getEl().swallowEvent("click");
16127         picker.container.addClass("x-menu-date-item");
16128     });
16129
16130     this.picker.on("select", this.onSelect, this);
16131 };
16132
16133 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
16134     // private
16135     onSelect : function(picker, date){
16136         this.fireEvent("select", this, date, picker);
16137         Roo.menu.DateItem.superclass.handleClick.call(this);
16138     }
16139 });/*
16140  * Based on:
16141  * Ext JS Library 1.1.1
16142  * Copyright(c) 2006-2007, Ext JS, LLC.
16143  *
16144  * Originally Released Under LGPL - original licence link has changed is not relivant.
16145  *
16146  * Fork - LGPL
16147  * <script type="text/javascript">
16148  */
16149  
16150 /**
16151  * @class Roo.menu.ColorItem
16152  * @extends Roo.menu.Adapter
16153  * A menu item that wraps the {@link Roo.ColorPalette} component.
16154  * @constructor
16155  * Creates a new ColorItem
16156  * @param {Object} config Configuration options
16157  */
16158 Roo.menu.ColorItem = function(config){
16159     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
16160     /** The Roo.ColorPalette object @type Roo.ColorPalette */
16161     this.palette = this.component;
16162     this.relayEvents(this.palette, ["select"]);
16163     if(this.selectHandler){
16164         this.on('select', this.selectHandler, this.scope);
16165     }
16166 };
16167 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
16168  * Based on:
16169  * Ext JS Library 1.1.1
16170  * Copyright(c) 2006-2007, Ext JS, LLC.
16171  *
16172  * Originally Released Under LGPL - original licence link has changed is not relivant.
16173  *
16174  * Fork - LGPL
16175  * <script type="text/javascript">
16176  */
16177  
16178
16179 /**
16180  * @class Roo.menu.DateMenu
16181  * @extends Roo.menu.Menu
16182  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
16183  * @constructor
16184  * Creates a new DateMenu
16185  * @param {Object} config Configuration options
16186  */
16187 Roo.menu.DateMenu = function(config){
16188     Roo.menu.DateMenu.superclass.constructor.call(this, config);
16189     this.plain = true;
16190     var di = new Roo.menu.DateItem(config);
16191     this.add(di);
16192     /**
16193      * The {@link Roo.DatePicker} instance for this DateMenu
16194      * @type DatePicker
16195      */
16196     this.picker = di.picker;
16197     /**
16198      * @event select
16199      * @param {DatePicker} picker
16200      * @param {Date} date
16201      */
16202     this.relayEvents(di, ["select"]);
16203     this.on('beforeshow', function(){
16204         if(this.picker){
16205             this.picker.hideMonthPicker(false);
16206         }
16207     }, this);
16208 };
16209 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
16210     cls:'x-date-menu'
16211 });/*
16212  * Based on:
16213  * Ext JS Library 1.1.1
16214  * Copyright(c) 2006-2007, Ext JS, LLC.
16215  *
16216  * Originally Released Under LGPL - original licence link has changed is not relivant.
16217  *
16218  * Fork - LGPL
16219  * <script type="text/javascript">
16220  */
16221  
16222
16223 /**
16224  * @class Roo.menu.ColorMenu
16225  * @extends Roo.menu.Menu
16226  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
16227  * @constructor
16228  * Creates a new ColorMenu
16229  * @param {Object} config Configuration options
16230  */
16231 Roo.menu.ColorMenu = function(config){
16232     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
16233     this.plain = true;
16234     var ci = new Roo.menu.ColorItem(config);
16235     this.add(ci);
16236     /**
16237      * The {@link Roo.ColorPalette} instance for this ColorMenu
16238      * @type ColorPalette
16239      */
16240     this.palette = ci.palette;
16241     /**
16242      * @event select
16243      * @param {ColorPalette} palette
16244      * @param {String} color
16245      */
16246     this.relayEvents(ci, ["select"]);
16247 };
16248 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
16249  * Based on:
16250  * Ext JS Library 1.1.1
16251  * Copyright(c) 2006-2007, Ext JS, LLC.
16252  *
16253  * Originally Released Under LGPL - original licence link has changed is not relivant.
16254  *
16255  * Fork - LGPL
16256  * <script type="text/javascript">
16257  */
16258  
16259 /**
16260  * @class Roo.form.TextItem
16261  * @extends Roo.BoxComponent
16262  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
16263  * @constructor
16264  * Creates a new TextItem
16265  * @param {Object} config Configuration options
16266  */
16267 Roo.form.TextItem = function(config){
16268     Roo.form.TextItem.superclass.constructor.call(this, config);
16269 };
16270
16271 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
16272     
16273     /**
16274      * @cfg {String} tag the tag for this item (default div)
16275      */
16276     tag : 'div',
16277     /**
16278      * @cfg {String} html the content for this item
16279      */
16280     html : '',
16281     
16282     getAutoCreate : function()
16283     {
16284         var cfg = {
16285             id: this.id,
16286             tag: this.tag,
16287             html: this.html
16288         };
16289         
16290         return cfg;
16291         
16292     }
16293     
16294 });/*
16295  * Based on:
16296  * Ext JS Library 1.1.1
16297  * Copyright(c) 2006-2007, Ext JS, LLC.
16298  *
16299  * Originally Released Under LGPL - original licence link has changed is not relivant.
16300  *
16301  * Fork - LGPL
16302  * <script type="text/javascript">
16303  */
16304  
16305 /**
16306  * @class Roo.form.Field
16307  * @extends Roo.BoxComponent
16308  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
16309  * @constructor
16310  * Creates a new Field
16311  * @param {Object} config Configuration options
16312  */
16313 Roo.form.Field = function(config){
16314     Roo.form.Field.superclass.constructor.call(this, config);
16315 };
16316
16317 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
16318     /**
16319      * @cfg {String} fieldLabel Label to use when rendering a form.
16320      */
16321        /**
16322      * @cfg {String} qtip Mouse over tip
16323      */
16324      
16325     /**
16326      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
16327      */
16328     invalidClass : "x-form-invalid",
16329     /**
16330      * @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")
16331      */
16332     invalidText : "The value in this field is invalid",
16333     /**
16334      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
16335      */
16336     focusClass : "x-form-focus",
16337     /**
16338      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
16339       automatic validation (defaults to "keyup").
16340      */
16341     validationEvent : "keyup",
16342     /**
16343      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
16344      */
16345     validateOnBlur : true,
16346     /**
16347      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
16348      */
16349     validationDelay : 250,
16350     /**
16351      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
16352      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
16353      */
16354     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
16355     /**
16356      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
16357      */
16358     fieldClass : "x-form-field",
16359     /**
16360      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
16361      *<pre>
16362 Value         Description
16363 -----------   ----------------------------------------------------------------------
16364 qtip          Display a quick tip when the user hovers over the field
16365 title         Display a default browser title attribute popup
16366 under         Add a block div beneath the field containing the error text
16367 side          Add an error icon to the right of the field with a popup on hover
16368 [element id]  Add the error text directly to the innerHTML of the specified element
16369 </pre>
16370      */
16371     msgTarget : 'qtip',
16372     /**
16373      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
16374      */
16375     msgFx : 'normal',
16376
16377     /**
16378      * @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.
16379      */
16380     readOnly : false,
16381
16382     /**
16383      * @cfg {Boolean} disabled True to disable the field (defaults to false).
16384      */
16385     disabled : false,
16386
16387     /**
16388      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
16389      */
16390     inputType : undefined,
16391     
16392     /**
16393      * @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).
16394          */
16395         tabIndex : undefined,
16396         
16397     // private
16398     isFormField : true,
16399
16400     // private
16401     hasFocus : false,
16402     /**
16403      * @property {Roo.Element} fieldEl
16404      * Element Containing the rendered Field (with label etc.)
16405      */
16406     /**
16407      * @cfg {Mixed} value A value to initialize this field with.
16408      */
16409     value : undefined,
16410
16411     /**
16412      * @cfg {String} name The field's HTML name attribute.
16413      */
16414     /**
16415      * @cfg {String} cls A CSS class to apply to the field's underlying element.
16416      */
16417     // private
16418     loadedValue : false,
16419      
16420      
16421         // private ??
16422         initComponent : function(){
16423         Roo.form.Field.superclass.initComponent.call(this);
16424         this.addEvents({
16425             /**
16426              * @event focus
16427              * Fires when this field receives input focus.
16428              * @param {Roo.form.Field} this
16429              */
16430             focus : true,
16431             /**
16432              * @event blur
16433              * Fires when this field loses input focus.
16434              * @param {Roo.form.Field} this
16435              */
16436             blur : true,
16437             /**
16438              * @event specialkey
16439              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
16440              * {@link Roo.EventObject#getKey} to determine which key was pressed.
16441              * @param {Roo.form.Field} this
16442              * @param {Roo.EventObject} e The event object
16443              */
16444             specialkey : true,
16445             /**
16446              * @event change
16447              * Fires just before the field blurs if the field value has changed.
16448              * @param {Roo.form.Field} this
16449              * @param {Mixed} newValue The new value
16450              * @param {Mixed} oldValue The original value
16451              */
16452             change : true,
16453             /**
16454              * @event invalid
16455              * Fires after the field has been marked as invalid.
16456              * @param {Roo.form.Field} this
16457              * @param {String} msg The validation message
16458              */
16459             invalid : true,
16460             /**
16461              * @event valid
16462              * Fires after the field has been validated with no errors.
16463              * @param {Roo.form.Field} this
16464              */
16465             valid : true,
16466              /**
16467              * @event keyup
16468              * Fires after the key up
16469              * @param {Roo.form.Field} this
16470              * @param {Roo.EventObject}  e The event Object
16471              */
16472             keyup : true
16473         });
16474     },
16475
16476     /**
16477      * Returns the name attribute of the field if available
16478      * @return {String} name The field name
16479      */
16480     getName: function(){
16481          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
16482     },
16483
16484     // private
16485     onRender : function(ct, position){
16486         Roo.form.Field.superclass.onRender.call(this, ct, position);
16487         if(!this.el){
16488             var cfg = this.getAutoCreate();
16489             if(!cfg.name){
16490                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
16491             }
16492             if (!cfg.name.length) {
16493                 delete cfg.name;
16494             }
16495             if(this.inputType){
16496                 cfg.type = this.inputType;
16497             }
16498             this.el = ct.createChild(cfg, position);
16499         }
16500         var type = this.el.dom.type;
16501         if(type){
16502             if(type == 'password'){
16503                 type = 'text';
16504             }
16505             this.el.addClass('x-form-'+type);
16506         }
16507         if(this.readOnly){
16508             this.el.dom.readOnly = true;
16509         }
16510         if(this.tabIndex !== undefined){
16511             this.el.dom.setAttribute('tabIndex', this.tabIndex);
16512         }
16513
16514         this.el.addClass([this.fieldClass, this.cls]);
16515         this.initValue();
16516     },
16517
16518     /**
16519      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
16520      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
16521      * @return {Roo.form.Field} this
16522      */
16523     applyTo : function(target){
16524         this.allowDomMove = false;
16525         this.el = Roo.get(target);
16526         this.render(this.el.dom.parentNode);
16527         return this;
16528     },
16529
16530     // private
16531     initValue : function(){
16532         if(this.value !== undefined){
16533             this.setValue(this.value);
16534         }else if(this.el.dom.value.length > 0){
16535             this.setValue(this.el.dom.value);
16536         }
16537     },
16538
16539     /**
16540      * Returns true if this field has been changed since it was originally loaded and is not disabled.
16541      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
16542      */
16543     isDirty : function() {
16544         if(this.disabled) {
16545             return false;
16546         }
16547         return String(this.getValue()) !== String(this.originalValue);
16548     },
16549
16550     /**
16551      * stores the current value in loadedValue
16552      */
16553     resetHasChanged : function()
16554     {
16555         this.loadedValue = String(this.getValue());
16556     },
16557     /**
16558      * checks the current value against the 'loaded' value.
16559      * Note - will return false if 'resetHasChanged' has not been called first.
16560      */
16561     hasChanged : function()
16562     {
16563         if(this.disabled || this.readOnly) {
16564             return false;
16565         }
16566         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
16567     },
16568     
16569     
16570     
16571     // private
16572     afterRender : function(){
16573         Roo.form.Field.superclass.afterRender.call(this);
16574         this.initEvents();
16575     },
16576
16577     // private
16578     fireKey : function(e){
16579         //Roo.log('field ' + e.getKey());
16580         if(e.isNavKeyPress()){
16581             this.fireEvent("specialkey", this, e);
16582         }
16583     },
16584
16585     /**
16586      * Resets the current field value to the originally loaded value and clears any validation messages
16587      */
16588     reset : function(){
16589         this.setValue(this.resetValue);
16590         this.originalValue = this.getValue();
16591         this.clearInvalid();
16592     },
16593
16594     // private
16595     initEvents : function(){
16596         // safari killled keypress - so keydown is now used..
16597         this.el.on("keydown" , this.fireKey,  this);
16598         this.el.on("focus", this.onFocus,  this);
16599         this.el.on("blur", this.onBlur,  this);
16600         this.el.relayEvent('keyup', this);
16601
16602         // reference to original value for reset
16603         this.originalValue = this.getValue();
16604         this.resetValue =  this.getValue();
16605     },
16606
16607     // private
16608     onFocus : function(){
16609         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16610             this.el.addClass(this.focusClass);
16611         }
16612         if(!this.hasFocus){
16613             this.hasFocus = true;
16614             this.startValue = this.getValue();
16615             this.fireEvent("focus", this);
16616         }
16617     },
16618
16619     beforeBlur : Roo.emptyFn,
16620
16621     // private
16622     onBlur : function(){
16623         this.beforeBlur();
16624         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16625             this.el.removeClass(this.focusClass);
16626         }
16627         this.hasFocus = false;
16628         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
16629             this.validate();
16630         }
16631         var v = this.getValue();
16632         if(String(v) !== String(this.startValue)){
16633             this.fireEvent('change', this, v, this.startValue);
16634         }
16635         this.fireEvent("blur", this);
16636     },
16637
16638     /**
16639      * Returns whether or not the field value is currently valid
16640      * @param {Boolean} preventMark True to disable marking the field invalid
16641      * @return {Boolean} True if the value is valid, else false
16642      */
16643     isValid : function(preventMark){
16644         if(this.disabled){
16645             return true;
16646         }
16647         var restore = this.preventMark;
16648         this.preventMark = preventMark === true;
16649         var v = this.validateValue(this.processValue(this.getRawValue()));
16650         this.preventMark = restore;
16651         return v;
16652     },
16653
16654     /**
16655      * Validates the field value
16656      * @return {Boolean} True if the value is valid, else false
16657      */
16658     validate : function(){
16659         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
16660             this.clearInvalid();
16661             return true;
16662         }
16663         return false;
16664     },
16665
16666     processValue : function(value){
16667         return value;
16668     },
16669
16670     // private
16671     // Subclasses should provide the validation implementation by overriding this
16672     validateValue : function(value){
16673         return true;
16674     },
16675
16676     /**
16677      * Mark this field as invalid
16678      * @param {String} msg The validation message
16679      */
16680     markInvalid : function(msg){
16681         if(!this.rendered || this.preventMark){ // not rendered
16682             return;
16683         }
16684         
16685         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16686         
16687         obj.el.addClass(this.invalidClass);
16688         msg = msg || this.invalidText;
16689         switch(this.msgTarget){
16690             case 'qtip':
16691                 obj.el.dom.qtip = msg;
16692                 obj.el.dom.qclass = 'x-form-invalid-tip';
16693                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
16694                     Roo.QuickTips.enable();
16695                 }
16696                 break;
16697             case 'title':
16698                 this.el.dom.title = msg;
16699                 break;
16700             case 'under':
16701                 if(!this.errorEl){
16702                     var elp = this.el.findParent('.x-form-element', 5, true);
16703                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
16704                     this.errorEl.setWidth(elp.getWidth(true)-20);
16705                 }
16706                 this.errorEl.update(msg);
16707                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
16708                 break;
16709             case 'side':
16710                 if(!this.errorIcon){
16711                     var elp = this.el.findParent('.x-form-element', 5, true);
16712                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
16713                 }
16714                 this.alignErrorIcon();
16715                 this.errorIcon.dom.qtip = msg;
16716                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
16717                 this.errorIcon.show();
16718                 this.on('resize', this.alignErrorIcon, this);
16719                 break;
16720             default:
16721                 var t = Roo.getDom(this.msgTarget);
16722                 t.innerHTML = msg;
16723                 t.style.display = this.msgDisplay;
16724                 break;
16725         }
16726         this.fireEvent('invalid', this, msg);
16727     },
16728
16729     // private
16730     alignErrorIcon : function(){
16731         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
16732     },
16733
16734     /**
16735      * Clear any invalid styles/messages for this field
16736      */
16737     clearInvalid : function(){
16738         if(!this.rendered || this.preventMark){ // not rendered
16739             return;
16740         }
16741         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16742         
16743         obj.el.removeClass(this.invalidClass);
16744         switch(this.msgTarget){
16745             case 'qtip':
16746                 obj.el.dom.qtip = '';
16747                 break;
16748             case 'title':
16749                 this.el.dom.title = '';
16750                 break;
16751             case 'under':
16752                 if(this.errorEl){
16753                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
16754                 }
16755                 break;
16756             case 'side':
16757                 if(this.errorIcon){
16758                     this.errorIcon.dom.qtip = '';
16759                     this.errorIcon.hide();
16760                     this.un('resize', this.alignErrorIcon, this);
16761                 }
16762                 break;
16763             default:
16764                 var t = Roo.getDom(this.msgTarget);
16765                 t.innerHTML = '';
16766                 t.style.display = 'none';
16767                 break;
16768         }
16769         this.fireEvent('valid', this);
16770     },
16771
16772     /**
16773      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
16774      * @return {Mixed} value The field value
16775      */
16776     getRawValue : function(){
16777         var v = this.el.getValue();
16778         
16779         return v;
16780     },
16781
16782     /**
16783      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16784      * @return {Mixed} value The field value
16785      */
16786     getValue : function(){
16787         var v = this.el.getValue();
16788          
16789         return v;
16790     },
16791
16792     /**
16793      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
16794      * @param {Mixed} value The value to set
16795      */
16796     setRawValue : function(v){
16797         return this.el.dom.value = (v === null || v === undefined ? '' : v);
16798     },
16799
16800     /**
16801      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
16802      * @param {Mixed} value The value to set
16803      */
16804     setValue : function(v){
16805         this.value = v;
16806         if(this.rendered){
16807             this.el.dom.value = (v === null || v === undefined ? '' : v);
16808              this.validate();
16809         }
16810     },
16811
16812     adjustSize : function(w, h){
16813         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
16814         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
16815         return s;
16816     },
16817
16818     adjustWidth : function(tag, w){
16819         tag = tag.toLowerCase();
16820         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
16821             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
16822                 if(tag == 'input'){
16823                     return w + 2;
16824                 }
16825                 if(tag == 'textarea'){
16826                     return w-2;
16827                 }
16828             }else if(Roo.isOpera){
16829                 if(tag == 'input'){
16830                     return w + 2;
16831                 }
16832                 if(tag == 'textarea'){
16833                     return w-2;
16834                 }
16835             }
16836         }
16837         return w;
16838     }
16839 });
16840
16841
16842 // anything other than normal should be considered experimental
16843 Roo.form.Field.msgFx = {
16844     normal : {
16845         show: function(msgEl, f){
16846             msgEl.setDisplayed('block');
16847         },
16848
16849         hide : function(msgEl, f){
16850             msgEl.setDisplayed(false).update('');
16851         }
16852     },
16853
16854     slide : {
16855         show: function(msgEl, f){
16856             msgEl.slideIn('t', {stopFx:true});
16857         },
16858
16859         hide : function(msgEl, f){
16860             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
16861         }
16862     },
16863
16864     slideRight : {
16865         show: function(msgEl, f){
16866             msgEl.fixDisplay();
16867             msgEl.alignTo(f.el, 'tl-tr');
16868             msgEl.slideIn('l', {stopFx:true});
16869         },
16870
16871         hide : function(msgEl, f){
16872             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
16873         }
16874     }
16875 };/*
16876  * Based on:
16877  * Ext JS Library 1.1.1
16878  * Copyright(c) 2006-2007, Ext JS, LLC.
16879  *
16880  * Originally Released Under LGPL - original licence link has changed is not relivant.
16881  *
16882  * Fork - LGPL
16883  * <script type="text/javascript">
16884  */
16885  
16886
16887 /**
16888  * @class Roo.form.TextField
16889  * @extends Roo.form.Field
16890  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
16891  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
16892  * @constructor
16893  * Creates a new TextField
16894  * @param {Object} config Configuration options
16895  */
16896 Roo.form.TextField = function(config){
16897     Roo.form.TextField.superclass.constructor.call(this, config);
16898     this.addEvents({
16899         /**
16900          * @event autosize
16901          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
16902          * according to the default logic, but this event provides a hook for the developer to apply additional
16903          * logic at runtime to resize the field if needed.
16904              * @param {Roo.form.Field} this This text field
16905              * @param {Number} width The new field width
16906              */
16907         autosize : true
16908     });
16909 };
16910
16911 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
16912     /**
16913      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
16914      */
16915     grow : false,
16916     /**
16917      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
16918      */
16919     growMin : 30,
16920     /**
16921      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
16922      */
16923     growMax : 800,
16924     /**
16925      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
16926      */
16927     vtype : null,
16928     /**
16929      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
16930      */
16931     maskRe : null,
16932     /**
16933      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
16934      */
16935     disableKeyFilter : false,
16936     /**
16937      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
16938      */
16939     allowBlank : true,
16940     /**
16941      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
16942      */
16943     minLength : 0,
16944     /**
16945      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
16946      */
16947     maxLength : Number.MAX_VALUE,
16948     /**
16949      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
16950      */
16951     minLengthText : "The minimum length for this field is {0}",
16952     /**
16953      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
16954      */
16955     maxLengthText : "The maximum length for this field is {0}",
16956     /**
16957      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
16958      */
16959     selectOnFocus : false,
16960     /**
16961      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
16962      */
16963     blankText : "This field is required",
16964     /**
16965      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
16966      * If available, this function will be called only after the basic validators all return true, and will be passed the
16967      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
16968      */
16969     validator : null,
16970     /**
16971      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
16972      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
16973      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
16974      */
16975     regex : null,
16976     /**
16977      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
16978      */
16979     regexText : "",
16980     /**
16981      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
16982      */
16983     emptyText : null,
16984    
16985
16986     // private
16987     initEvents : function()
16988     {
16989         if (this.emptyText) {
16990             this.el.attr('placeholder', this.emptyText);
16991         }
16992         
16993         Roo.form.TextField.superclass.initEvents.call(this);
16994         if(this.validationEvent == 'keyup'){
16995             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
16996             this.el.on('keyup', this.filterValidation, this);
16997         }
16998         else if(this.validationEvent !== false){
16999             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
17000         }
17001         
17002         if(this.selectOnFocus){
17003             this.on("focus", this.preFocus, this);
17004             
17005         }
17006         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
17007             this.el.on("keypress", this.filterKeys, this);
17008         }
17009         if(this.grow){
17010             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
17011             this.el.on("click", this.autoSize,  this);
17012         }
17013         if(this.el.is('input[type=password]') && Roo.isSafari){
17014             this.el.on('keydown', this.SafariOnKeyDown, this);
17015         }
17016     },
17017
17018     processValue : function(value){
17019         if(this.stripCharsRe){
17020             var newValue = value.replace(this.stripCharsRe, '');
17021             if(newValue !== value){
17022                 this.setRawValue(newValue);
17023                 return newValue;
17024             }
17025         }
17026         return value;
17027     },
17028
17029     filterValidation : function(e){
17030         if(!e.isNavKeyPress()){
17031             this.validationTask.delay(this.validationDelay);
17032         }
17033     },
17034
17035     // private
17036     onKeyUp : function(e){
17037         if(!e.isNavKeyPress()){
17038             this.autoSize();
17039         }
17040     },
17041
17042     /**
17043      * Resets the current field value to the originally-loaded value and clears any validation messages.
17044      *  
17045      */
17046     reset : function(){
17047         Roo.form.TextField.superclass.reset.call(this);
17048        
17049     },
17050
17051     
17052     // private
17053     preFocus : function(){
17054         
17055         if(this.selectOnFocus){
17056             this.el.dom.select();
17057         }
17058     },
17059
17060     
17061     // private
17062     filterKeys : function(e){
17063         var k = e.getKey();
17064         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
17065             return;
17066         }
17067         var c = e.getCharCode(), cc = String.fromCharCode(c);
17068         if(Roo.isIE && (e.isSpecialKey() || !cc)){
17069             return;
17070         }
17071         if(!this.maskRe.test(cc)){
17072             e.stopEvent();
17073         }
17074     },
17075
17076     setValue : function(v){
17077         
17078         Roo.form.TextField.superclass.setValue.apply(this, arguments);
17079         
17080         this.autoSize();
17081     },
17082
17083     /**
17084      * Validates a value according to the field's validation rules and marks the field as invalid
17085      * if the validation fails
17086      * @param {Mixed} value The value to validate
17087      * @return {Boolean} True if the value is valid, else false
17088      */
17089     validateValue : function(value){
17090         if(value.length < 1)  { // if it's blank
17091              if(this.allowBlank){
17092                 this.clearInvalid();
17093                 return true;
17094              }else{
17095                 this.markInvalid(this.blankText);
17096                 return false;
17097              }
17098         }
17099         if(value.length < this.minLength){
17100             this.markInvalid(String.format(this.minLengthText, this.minLength));
17101             return false;
17102         }
17103         if(value.length > this.maxLength){
17104             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
17105             return false;
17106         }
17107         if(this.vtype){
17108             var vt = Roo.form.VTypes;
17109             if(!vt[this.vtype](value, this)){
17110                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
17111                 return false;
17112             }
17113         }
17114         if(typeof this.validator == "function"){
17115             var msg = this.validator(value);
17116             if(msg !== true){
17117                 this.markInvalid(msg);
17118                 return false;
17119             }
17120         }
17121         if(this.regex && !this.regex.test(value)){
17122             this.markInvalid(this.regexText);
17123             return false;
17124         }
17125         return true;
17126     },
17127
17128     /**
17129      * Selects text in this field
17130      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
17131      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
17132      */
17133     selectText : function(start, end){
17134         var v = this.getRawValue();
17135         if(v.length > 0){
17136             start = start === undefined ? 0 : start;
17137             end = end === undefined ? v.length : end;
17138             var d = this.el.dom;
17139             if(d.setSelectionRange){
17140                 d.setSelectionRange(start, end);
17141             }else if(d.createTextRange){
17142                 var range = d.createTextRange();
17143                 range.moveStart("character", start);
17144                 range.moveEnd("character", v.length-end);
17145                 range.select();
17146             }
17147         }
17148     },
17149
17150     /**
17151      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
17152      * This only takes effect if grow = true, and fires the autosize event.
17153      */
17154     autoSize : function(){
17155         if(!this.grow || !this.rendered){
17156             return;
17157         }
17158         if(!this.metrics){
17159             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
17160         }
17161         var el = this.el;
17162         var v = el.dom.value;
17163         var d = document.createElement('div');
17164         d.appendChild(document.createTextNode(v));
17165         v = d.innerHTML;
17166         d = null;
17167         v += "&#160;";
17168         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
17169         this.el.setWidth(w);
17170         this.fireEvent("autosize", this, w);
17171     },
17172     
17173     // private
17174     SafariOnKeyDown : function(event)
17175     {
17176         // this is a workaround for a password hang bug on chrome/ webkit.
17177         
17178         var isSelectAll = false;
17179         
17180         if(this.el.dom.selectionEnd > 0){
17181             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
17182         }
17183         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
17184             event.preventDefault();
17185             this.setValue('');
17186             return;
17187         }
17188         
17189         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
17190             
17191             event.preventDefault();
17192             // this is very hacky as keydown always get's upper case.
17193             
17194             var cc = String.fromCharCode(event.getCharCode());
17195             
17196             
17197             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
17198             
17199         }
17200         
17201         
17202     }
17203 });/*
17204  * Based on:
17205  * Ext JS Library 1.1.1
17206  * Copyright(c) 2006-2007, Ext JS, LLC.
17207  *
17208  * Originally Released Under LGPL - original licence link has changed is not relivant.
17209  *
17210  * Fork - LGPL
17211  * <script type="text/javascript">
17212  */
17213  
17214 /**
17215  * @class Roo.form.Hidden
17216  * @extends Roo.form.TextField
17217  * Simple Hidden element used on forms 
17218  * 
17219  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
17220  * 
17221  * @constructor
17222  * Creates a new Hidden form element.
17223  * @param {Object} config Configuration options
17224  */
17225
17226
17227
17228 // easy hidden field...
17229 Roo.form.Hidden = function(config){
17230     Roo.form.Hidden.superclass.constructor.call(this, config);
17231 };
17232   
17233 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
17234     fieldLabel:      '',
17235     inputType:      'hidden',
17236     width:          50,
17237     allowBlank:     true,
17238     labelSeparator: '',
17239     hidden:         true,
17240     itemCls :       'x-form-item-display-none'
17241
17242
17243 });
17244
17245
17246 /*
17247  * Based on:
17248  * Ext JS Library 1.1.1
17249  * Copyright(c) 2006-2007, Ext JS, LLC.
17250  *
17251  * Originally Released Under LGPL - original licence link has changed is not relivant.
17252  *
17253  * Fork - LGPL
17254  * <script type="text/javascript">
17255  */
17256  
17257 /**
17258  * @class Roo.form.TriggerField
17259  * @extends Roo.form.TextField
17260  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
17261  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
17262  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
17263  * for which you can provide a custom implementation.  For example:
17264  * <pre><code>
17265 var trigger = new Roo.form.TriggerField();
17266 trigger.onTriggerClick = myTriggerFn;
17267 trigger.applyTo('my-field');
17268 </code></pre>
17269  *
17270  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
17271  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
17272  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
17273  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
17274  * @constructor
17275  * Create a new TriggerField.
17276  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
17277  * to the base TextField)
17278  */
17279 Roo.form.TriggerField = function(config){
17280     this.mimicing = false;
17281     Roo.form.TriggerField.superclass.constructor.call(this, config);
17282 };
17283
17284 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
17285     /**
17286      * @cfg {String} triggerClass A CSS class to apply to the trigger
17287      */
17288     /**
17289      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17290      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
17291      */
17292     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
17293     /**
17294      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
17295      */
17296     hideTrigger:false,
17297
17298     /** @cfg {Boolean} grow @hide */
17299     /** @cfg {Number} growMin @hide */
17300     /** @cfg {Number} growMax @hide */
17301
17302     /**
17303      * @hide 
17304      * @method
17305      */
17306     autoSize: Roo.emptyFn,
17307     // private
17308     monitorTab : true,
17309     // private
17310     deferHeight : true,
17311
17312     
17313     actionMode : 'wrap',
17314     // private
17315     onResize : function(w, h){
17316         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
17317         if(typeof w == 'number'){
17318             var x = w - this.trigger.getWidth();
17319             this.el.setWidth(this.adjustWidth('input', x));
17320             this.trigger.setStyle('left', x+'px');
17321         }
17322     },
17323
17324     // private
17325     adjustSize : Roo.BoxComponent.prototype.adjustSize,
17326
17327     // private
17328     getResizeEl : function(){
17329         return this.wrap;
17330     },
17331
17332     // private
17333     getPositionEl : function(){
17334         return this.wrap;
17335     },
17336
17337     // private
17338     alignErrorIcon : function(){
17339         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
17340     },
17341
17342     // private
17343     onRender : function(ct, position){
17344         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
17345         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
17346         this.trigger = this.wrap.createChild(this.triggerConfig ||
17347                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
17348         if(this.hideTrigger){
17349             this.trigger.setDisplayed(false);
17350         }
17351         this.initTrigger();
17352         if(!this.width){
17353             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
17354         }
17355     },
17356
17357     // private
17358     initTrigger : function(){
17359         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
17360         this.trigger.addClassOnOver('x-form-trigger-over');
17361         this.trigger.addClassOnClick('x-form-trigger-click');
17362     },
17363
17364     // private
17365     onDestroy : function(){
17366         if(this.trigger){
17367             this.trigger.removeAllListeners();
17368             this.trigger.remove();
17369         }
17370         if(this.wrap){
17371             this.wrap.remove();
17372         }
17373         Roo.form.TriggerField.superclass.onDestroy.call(this);
17374     },
17375
17376     // private
17377     onFocus : function(){
17378         Roo.form.TriggerField.superclass.onFocus.call(this);
17379         if(!this.mimicing){
17380             this.wrap.addClass('x-trigger-wrap-focus');
17381             this.mimicing = true;
17382             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
17383             if(this.monitorTab){
17384                 this.el.on("keydown", this.checkTab, this);
17385             }
17386         }
17387     },
17388
17389     // private
17390     checkTab : function(e){
17391         if(e.getKey() == e.TAB){
17392             this.triggerBlur();
17393         }
17394     },
17395
17396     // private
17397     onBlur : function(){
17398         // do nothing
17399     },
17400
17401     // private
17402     mimicBlur : function(e, t){
17403         if(!this.wrap.contains(t) && this.validateBlur()){
17404             this.triggerBlur();
17405         }
17406     },
17407
17408     // private
17409     triggerBlur : function(){
17410         this.mimicing = false;
17411         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
17412         if(this.monitorTab){
17413             this.el.un("keydown", this.checkTab, this);
17414         }
17415         this.wrap.removeClass('x-trigger-wrap-focus');
17416         Roo.form.TriggerField.superclass.onBlur.call(this);
17417     },
17418
17419     // private
17420     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
17421     validateBlur : function(e, t){
17422         return true;
17423     },
17424
17425     // private
17426     onDisable : function(){
17427         Roo.form.TriggerField.superclass.onDisable.call(this);
17428         if(this.wrap){
17429             this.wrap.addClass('x-item-disabled');
17430         }
17431     },
17432
17433     // private
17434     onEnable : function(){
17435         Roo.form.TriggerField.superclass.onEnable.call(this);
17436         if(this.wrap){
17437             this.wrap.removeClass('x-item-disabled');
17438         }
17439     },
17440
17441     // private
17442     onShow : function(){
17443         var ae = this.getActionEl();
17444         
17445         if(ae){
17446             ae.dom.style.display = '';
17447             ae.dom.style.visibility = 'visible';
17448         }
17449     },
17450
17451     // private
17452     
17453     onHide : function(){
17454         var ae = this.getActionEl();
17455         ae.dom.style.display = 'none';
17456     },
17457
17458     /**
17459      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
17460      * by an implementing function.
17461      * @method
17462      * @param {EventObject} e
17463      */
17464     onTriggerClick : Roo.emptyFn
17465 });
17466
17467 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
17468 // to be extended by an implementing class.  For an example of implementing this class, see the custom
17469 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
17470 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
17471     initComponent : function(){
17472         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
17473
17474         this.triggerConfig = {
17475             tag:'span', cls:'x-form-twin-triggers', cn:[
17476             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
17477             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
17478         ]};
17479     },
17480
17481     getTrigger : function(index){
17482         return this.triggers[index];
17483     },
17484
17485     initTrigger : function(){
17486         var ts = this.trigger.select('.x-form-trigger', true);
17487         this.wrap.setStyle('overflow', 'hidden');
17488         var triggerField = this;
17489         ts.each(function(t, all, index){
17490             t.hide = function(){
17491                 var w = triggerField.wrap.getWidth();
17492                 this.dom.style.display = 'none';
17493                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17494             };
17495             t.show = function(){
17496                 var w = triggerField.wrap.getWidth();
17497                 this.dom.style.display = '';
17498                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17499             };
17500             var triggerIndex = 'Trigger'+(index+1);
17501
17502             if(this['hide'+triggerIndex]){
17503                 t.dom.style.display = 'none';
17504             }
17505             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
17506             t.addClassOnOver('x-form-trigger-over');
17507             t.addClassOnClick('x-form-trigger-click');
17508         }, this);
17509         this.triggers = ts.elements;
17510     },
17511
17512     onTrigger1Click : Roo.emptyFn,
17513     onTrigger2Click : Roo.emptyFn
17514 });/*
17515  * Based on:
17516  * Ext JS Library 1.1.1
17517  * Copyright(c) 2006-2007, Ext JS, LLC.
17518  *
17519  * Originally Released Under LGPL - original licence link has changed is not relivant.
17520  *
17521  * Fork - LGPL
17522  * <script type="text/javascript">
17523  */
17524  
17525 /**
17526  * @class Roo.form.TextArea
17527  * @extends Roo.form.TextField
17528  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
17529  * support for auto-sizing.
17530  * @constructor
17531  * Creates a new TextArea
17532  * @param {Object} config Configuration options
17533  */
17534 Roo.form.TextArea = function(config){
17535     Roo.form.TextArea.superclass.constructor.call(this, config);
17536     // these are provided exchanges for backwards compat
17537     // minHeight/maxHeight were replaced by growMin/growMax to be
17538     // compatible with TextField growing config values
17539     if(this.minHeight !== undefined){
17540         this.growMin = this.minHeight;
17541     }
17542     if(this.maxHeight !== undefined){
17543         this.growMax = this.maxHeight;
17544     }
17545 };
17546
17547 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
17548     /**
17549      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
17550      */
17551     growMin : 60,
17552     /**
17553      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
17554      */
17555     growMax: 1000,
17556     /**
17557      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
17558      * in the field (equivalent to setting overflow: hidden, defaults to false)
17559      */
17560     preventScrollbars: false,
17561     /**
17562      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17563      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
17564      */
17565
17566     // private
17567     onRender : function(ct, position){
17568         if(!this.el){
17569             this.defaultAutoCreate = {
17570                 tag: "textarea",
17571                 style:"width:300px;height:60px;",
17572                 autocomplete: "new-password"
17573             };
17574         }
17575         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
17576         if(this.grow){
17577             this.textSizeEl = Roo.DomHelper.append(document.body, {
17578                 tag: "pre", cls: "x-form-grow-sizer"
17579             });
17580             if(this.preventScrollbars){
17581                 this.el.setStyle("overflow", "hidden");
17582             }
17583             this.el.setHeight(this.growMin);
17584         }
17585     },
17586
17587     onDestroy : function(){
17588         if(this.textSizeEl){
17589             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
17590         }
17591         Roo.form.TextArea.superclass.onDestroy.call(this);
17592     },
17593
17594     // private
17595     onKeyUp : function(e){
17596         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
17597             this.autoSize();
17598         }
17599     },
17600
17601     /**
17602      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
17603      * This only takes effect if grow = true, and fires the autosize event if the height changes.
17604      */
17605     autoSize : function(){
17606         if(!this.grow || !this.textSizeEl){
17607             return;
17608         }
17609         var el = this.el;
17610         var v = el.dom.value;
17611         var ts = this.textSizeEl;
17612
17613         ts.innerHTML = '';
17614         ts.appendChild(document.createTextNode(v));
17615         v = ts.innerHTML;
17616
17617         Roo.fly(ts).setWidth(this.el.getWidth());
17618         if(v.length < 1){
17619             v = "&#160;&#160;";
17620         }else{
17621             if(Roo.isIE){
17622                 v = v.replace(/\n/g, '<p>&#160;</p>');
17623             }
17624             v += "&#160;\n&#160;";
17625         }
17626         ts.innerHTML = v;
17627         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
17628         if(h != this.lastHeight){
17629             this.lastHeight = h;
17630             this.el.setHeight(h);
17631             this.fireEvent("autosize", this, h);
17632         }
17633     }
17634 });/*
17635  * Based on:
17636  * Ext JS Library 1.1.1
17637  * Copyright(c) 2006-2007, Ext JS, LLC.
17638  *
17639  * Originally Released Under LGPL - original licence link has changed is not relivant.
17640  *
17641  * Fork - LGPL
17642  * <script type="text/javascript">
17643  */
17644  
17645
17646 /**
17647  * @class Roo.form.NumberField
17648  * @extends Roo.form.TextField
17649  * Numeric text field that provides automatic keystroke filtering and numeric validation.
17650  * @constructor
17651  * Creates a new NumberField
17652  * @param {Object} config Configuration options
17653  */
17654 Roo.form.NumberField = function(config){
17655     Roo.form.NumberField.superclass.constructor.call(this, config);
17656 };
17657
17658 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
17659     /**
17660      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
17661      */
17662     fieldClass: "x-form-field x-form-num-field",
17663     /**
17664      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
17665      */
17666     allowDecimals : true,
17667     /**
17668      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
17669      */
17670     decimalSeparator : ".",
17671     /**
17672      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
17673      */
17674     decimalPrecision : 2,
17675     /**
17676      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
17677      */
17678     allowNegative : true,
17679     /**
17680      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
17681      */
17682     minValue : Number.NEGATIVE_INFINITY,
17683     /**
17684      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
17685      */
17686     maxValue : Number.MAX_VALUE,
17687     /**
17688      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
17689      */
17690     minText : "The minimum value for this field is {0}",
17691     /**
17692      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
17693      */
17694     maxText : "The maximum value for this field is {0}",
17695     /**
17696      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
17697      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
17698      */
17699     nanText : "{0} is not a valid number",
17700
17701     // private
17702     initEvents : function(){
17703         Roo.form.NumberField.superclass.initEvents.call(this);
17704         var allowed = "0123456789";
17705         if(this.allowDecimals){
17706             allowed += this.decimalSeparator;
17707         }
17708         if(this.allowNegative){
17709             allowed += "-";
17710         }
17711         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
17712         var keyPress = function(e){
17713             var k = e.getKey();
17714             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
17715                 return;
17716             }
17717             var c = e.getCharCode();
17718             if(allowed.indexOf(String.fromCharCode(c)) === -1){
17719                 e.stopEvent();
17720             }
17721         };
17722         this.el.on("keypress", keyPress, this);
17723     },
17724
17725     // private
17726     validateValue : function(value){
17727         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
17728             return false;
17729         }
17730         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17731              return true;
17732         }
17733         var num = this.parseValue(value);
17734         if(isNaN(num)){
17735             this.markInvalid(String.format(this.nanText, value));
17736             return false;
17737         }
17738         if(num < this.minValue){
17739             this.markInvalid(String.format(this.minText, this.minValue));
17740             return false;
17741         }
17742         if(num > this.maxValue){
17743             this.markInvalid(String.format(this.maxText, this.maxValue));
17744             return false;
17745         }
17746         return true;
17747     },
17748
17749     getValue : function(){
17750         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
17751     },
17752
17753     // private
17754     parseValue : function(value){
17755         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
17756         return isNaN(value) ? '' : value;
17757     },
17758
17759     // private
17760     fixPrecision : function(value){
17761         var nan = isNaN(value);
17762         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
17763             return nan ? '' : value;
17764         }
17765         return parseFloat(value).toFixed(this.decimalPrecision);
17766     },
17767
17768     setValue : function(v){
17769         v = this.fixPrecision(v);
17770         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
17771     },
17772
17773     // private
17774     decimalPrecisionFcn : function(v){
17775         return Math.floor(v);
17776     },
17777
17778     beforeBlur : function(){
17779         var v = this.parseValue(this.getRawValue());
17780         if(v){
17781             this.setValue(v);
17782         }
17783     }
17784 });/*
17785  * Based on:
17786  * Ext JS Library 1.1.1
17787  * Copyright(c) 2006-2007, Ext JS, LLC.
17788  *
17789  * Originally Released Under LGPL - original licence link has changed is not relivant.
17790  *
17791  * Fork - LGPL
17792  * <script type="text/javascript">
17793  */
17794  
17795 /**
17796  * @class Roo.form.DateField
17797  * @extends Roo.form.TriggerField
17798  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17799 * @constructor
17800 * Create a new DateField
17801 * @param {Object} config
17802  */
17803 Roo.form.DateField = function(config){
17804     Roo.form.DateField.superclass.constructor.call(this, config);
17805     
17806       this.addEvents({
17807          
17808         /**
17809          * @event select
17810          * Fires when a date is selected
17811              * @param {Roo.form.DateField} combo This combo box
17812              * @param {Date} date The date selected
17813              */
17814         'select' : true
17815          
17816     });
17817     
17818     
17819     if(typeof this.minValue == "string") {
17820         this.minValue = this.parseDate(this.minValue);
17821     }
17822     if(typeof this.maxValue == "string") {
17823         this.maxValue = this.parseDate(this.maxValue);
17824     }
17825     this.ddMatch = null;
17826     if(this.disabledDates){
17827         var dd = this.disabledDates;
17828         var re = "(?:";
17829         for(var i = 0; i < dd.length; i++){
17830             re += dd[i];
17831             if(i != dd.length-1) {
17832                 re += "|";
17833             }
17834         }
17835         this.ddMatch = new RegExp(re + ")");
17836     }
17837 };
17838
17839 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
17840     /**
17841      * @cfg {String} format
17842      * The default date format string which can be overriden for localization support.  The format must be
17843      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17844      */
17845     format : "m/d/y",
17846     /**
17847      * @cfg {String} altFormats
17848      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17849      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17850      */
17851     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17852     /**
17853      * @cfg {Array} disabledDays
17854      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17855      */
17856     disabledDays : null,
17857     /**
17858      * @cfg {String} disabledDaysText
17859      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17860      */
17861     disabledDaysText : "Disabled",
17862     /**
17863      * @cfg {Array} disabledDates
17864      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17865      * expression so they are very powerful. Some examples:
17866      * <ul>
17867      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17868      * <li>["03/08", "09/16"] would disable those days for every year</li>
17869      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17870      * <li>["03/../2006"] would disable every day in March 2006</li>
17871      * <li>["^03"] would disable every day in every March</li>
17872      * </ul>
17873      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17874      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17875      */
17876     disabledDates : null,
17877     /**
17878      * @cfg {String} disabledDatesText
17879      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17880      */
17881     disabledDatesText : "Disabled",
17882     /**
17883      * @cfg {Date/String} minValue
17884      * The minimum allowed date. Can be either a Javascript date object or a string date in a
17885      * valid format (defaults to null).
17886      */
17887     minValue : null,
17888     /**
17889      * @cfg {Date/String} maxValue
17890      * The maximum allowed date. Can be either a Javascript date object or a string date in a
17891      * valid format (defaults to null).
17892      */
17893     maxValue : null,
17894     /**
17895      * @cfg {String} minText
17896      * The error text to display when the date in the cell is before minValue (defaults to
17897      * 'The date in this field must be after {minValue}').
17898      */
17899     minText : "The date in this field must be equal to or after {0}",
17900     /**
17901      * @cfg {String} maxText
17902      * The error text to display when the date in the cell is after maxValue (defaults to
17903      * 'The date in this field must be before {maxValue}').
17904      */
17905     maxText : "The date in this field must be equal to or before {0}",
17906     /**
17907      * @cfg {String} invalidText
17908      * The error text to display when the date in the field is invalid (defaults to
17909      * '{value} is not a valid date - it must be in the format {format}').
17910      */
17911     invalidText : "{0} is not a valid date - it must be in the format {1}",
17912     /**
17913      * @cfg {String} triggerClass
17914      * An additional CSS class used to style the trigger button.  The trigger will always get the
17915      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
17916      * which displays a calendar icon).
17917      */
17918     triggerClass : 'x-form-date-trigger',
17919     
17920
17921     /**
17922      * @cfg {Boolean} useIso
17923      * if enabled, then the date field will use a hidden field to store the 
17924      * real value as iso formated date. default (false)
17925      */ 
17926     useIso : false,
17927     /**
17928      * @cfg {String/Object} autoCreate
17929      * A DomHelper element spec, or true for a default element spec (defaults to
17930      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
17931      */ 
17932     // private
17933     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
17934     
17935     // private
17936     hiddenField: false,
17937     
17938     onRender : function(ct, position)
17939     {
17940         Roo.form.DateField.superclass.onRender.call(this, ct, position);
17941         if (this.useIso) {
17942             //this.el.dom.removeAttribute('name'); 
17943             Roo.log("Changing name?");
17944             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
17945             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
17946                     'before', true);
17947             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
17948             // prevent input submission
17949             this.hiddenName = this.name;
17950         }
17951             
17952             
17953     },
17954     
17955     // private
17956     validateValue : function(value)
17957     {
17958         value = this.formatDate(value);
17959         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
17960             Roo.log('super failed');
17961             return false;
17962         }
17963         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17964              return true;
17965         }
17966         var svalue = value;
17967         value = this.parseDate(value);
17968         if(!value){
17969             Roo.log('parse date failed' + svalue);
17970             this.markInvalid(String.format(this.invalidText, svalue, this.format));
17971             return false;
17972         }
17973         var time = value.getTime();
17974         if(this.minValue && time < this.minValue.getTime()){
17975             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
17976             return false;
17977         }
17978         if(this.maxValue && time > this.maxValue.getTime()){
17979             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
17980             return false;
17981         }
17982         if(this.disabledDays){
17983             var day = value.getDay();
17984             for(var i = 0; i < this.disabledDays.length; i++) {
17985                 if(day === this.disabledDays[i]){
17986                     this.markInvalid(this.disabledDaysText);
17987                     return false;
17988                 }
17989             }
17990         }
17991         var fvalue = this.formatDate(value);
17992         if(this.ddMatch && this.ddMatch.test(fvalue)){
17993             this.markInvalid(String.format(this.disabledDatesText, fvalue));
17994             return false;
17995         }
17996         return true;
17997     },
17998
17999     // private
18000     // Provides logic to override the default TriggerField.validateBlur which just returns true
18001     validateBlur : function(){
18002         return !this.menu || !this.menu.isVisible();
18003     },
18004     
18005     getName: function()
18006     {
18007         // returns hidden if it's set..
18008         if (!this.rendered) {return ''};
18009         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
18010         
18011     },
18012
18013     /**
18014      * Returns the current date value of the date field.
18015      * @return {Date} The date value
18016      */
18017     getValue : function(){
18018         
18019         return  this.hiddenField ?
18020                 this.hiddenField.value :
18021                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
18022     },
18023
18024     /**
18025      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
18026      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
18027      * (the default format used is "m/d/y").
18028      * <br />Usage:
18029      * <pre><code>
18030 //All of these calls set the same date value (May 4, 2006)
18031
18032 //Pass a date object:
18033 var dt = new Date('5/4/06');
18034 dateField.setValue(dt);
18035
18036 //Pass a date string (default format):
18037 dateField.setValue('5/4/06');
18038
18039 //Pass a date string (custom format):
18040 dateField.format = 'Y-m-d';
18041 dateField.setValue('2006-5-4');
18042 </code></pre>
18043      * @param {String/Date} date The date or valid date string
18044      */
18045     setValue : function(date){
18046         if (this.hiddenField) {
18047             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
18048         }
18049         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
18050         // make sure the value field is always stored as a date..
18051         this.value = this.parseDate(date);
18052         
18053         
18054     },
18055
18056     // private
18057     parseDate : function(value){
18058         if(!value || value instanceof Date){
18059             return value;
18060         }
18061         var v = Date.parseDate(value, this.format);
18062          if (!v && this.useIso) {
18063             v = Date.parseDate(value, 'Y-m-d');
18064         }
18065         if(!v && this.altFormats){
18066             if(!this.altFormatsArray){
18067                 this.altFormatsArray = this.altFormats.split("|");
18068             }
18069             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18070                 v = Date.parseDate(value, this.altFormatsArray[i]);
18071             }
18072         }
18073         return v;
18074     },
18075
18076     // private
18077     formatDate : function(date, fmt){
18078         return (!date || !(date instanceof Date)) ?
18079                date : date.dateFormat(fmt || this.format);
18080     },
18081
18082     // private
18083     menuListeners : {
18084         select: function(m, d){
18085             
18086             this.setValue(d);
18087             this.fireEvent('select', this, d);
18088         },
18089         show : function(){ // retain focus styling
18090             this.onFocus();
18091         },
18092         hide : function(){
18093             this.focus.defer(10, this);
18094             var ml = this.menuListeners;
18095             this.menu.un("select", ml.select,  this);
18096             this.menu.un("show", ml.show,  this);
18097             this.menu.un("hide", ml.hide,  this);
18098         }
18099     },
18100
18101     // private
18102     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
18103     onTriggerClick : function(){
18104         if(this.disabled){
18105             return;
18106         }
18107         if(this.menu == null){
18108             this.menu = new Roo.menu.DateMenu();
18109         }
18110         Roo.apply(this.menu.picker,  {
18111             showClear: this.allowBlank,
18112             minDate : this.minValue,
18113             maxDate : this.maxValue,
18114             disabledDatesRE : this.ddMatch,
18115             disabledDatesText : this.disabledDatesText,
18116             disabledDays : this.disabledDays,
18117             disabledDaysText : this.disabledDaysText,
18118             format : this.useIso ? 'Y-m-d' : this.format,
18119             minText : String.format(this.minText, this.formatDate(this.minValue)),
18120             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
18121         });
18122         this.menu.on(Roo.apply({}, this.menuListeners, {
18123             scope:this
18124         }));
18125         this.menu.picker.setValue(this.getValue() || new Date());
18126         this.menu.show(this.el, "tl-bl?");
18127     },
18128
18129     beforeBlur : function(){
18130         var v = this.parseDate(this.getRawValue());
18131         if(v){
18132             this.setValue(v);
18133         }
18134     },
18135
18136     /*@
18137      * overide
18138      * 
18139      */
18140     isDirty : function() {
18141         if(this.disabled) {
18142             return false;
18143         }
18144         
18145         if(typeof(this.startValue) === 'undefined'){
18146             return false;
18147         }
18148         
18149         return String(this.getValue()) !== String(this.startValue);
18150         
18151     }
18152 });/*
18153  * Based on:
18154  * Ext JS Library 1.1.1
18155  * Copyright(c) 2006-2007, Ext JS, LLC.
18156  *
18157  * Originally Released Under LGPL - original licence link has changed is not relivant.
18158  *
18159  * Fork - LGPL
18160  * <script type="text/javascript">
18161  */
18162  
18163 /**
18164  * @class Roo.form.MonthField
18165  * @extends Roo.form.TriggerField
18166  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
18167 * @constructor
18168 * Create a new MonthField
18169 * @param {Object} config
18170  */
18171 Roo.form.MonthField = function(config){
18172     
18173     Roo.form.MonthField.superclass.constructor.call(this, config);
18174     
18175       this.addEvents({
18176          
18177         /**
18178          * @event select
18179          * Fires when a date is selected
18180              * @param {Roo.form.MonthFieeld} combo This combo box
18181              * @param {Date} date The date selected
18182              */
18183         'select' : true
18184          
18185     });
18186     
18187     
18188     if(typeof this.minValue == "string") {
18189         this.minValue = this.parseDate(this.minValue);
18190     }
18191     if(typeof this.maxValue == "string") {
18192         this.maxValue = this.parseDate(this.maxValue);
18193     }
18194     this.ddMatch = null;
18195     if(this.disabledDates){
18196         var dd = this.disabledDates;
18197         var re = "(?:";
18198         for(var i = 0; i < dd.length; i++){
18199             re += dd[i];
18200             if(i != dd.length-1) {
18201                 re += "|";
18202             }
18203         }
18204         this.ddMatch = new RegExp(re + ")");
18205     }
18206 };
18207
18208 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
18209     /**
18210      * @cfg {String} format
18211      * The default date format string which can be overriden for localization support.  The format must be
18212      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18213      */
18214     format : "M Y",
18215     /**
18216      * @cfg {String} altFormats
18217      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18218      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18219      */
18220     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
18221     /**
18222      * @cfg {Array} disabledDays
18223      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
18224      */
18225     disabledDays : [0,1,2,3,4,5,6],
18226     /**
18227      * @cfg {String} disabledDaysText
18228      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
18229      */
18230     disabledDaysText : "Disabled",
18231     /**
18232      * @cfg {Array} disabledDates
18233      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
18234      * expression so they are very powerful. Some examples:
18235      * <ul>
18236      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
18237      * <li>["03/08", "09/16"] would disable those days for every year</li>
18238      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
18239      * <li>["03/../2006"] would disable every day in March 2006</li>
18240      * <li>["^03"] would disable every day in every March</li>
18241      * </ul>
18242      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
18243      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
18244      */
18245     disabledDates : null,
18246     /**
18247      * @cfg {String} disabledDatesText
18248      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
18249      */
18250     disabledDatesText : "Disabled",
18251     /**
18252      * @cfg {Date/String} minValue
18253      * The minimum allowed date. Can be either a Javascript date object or a string date in a
18254      * valid format (defaults to null).
18255      */
18256     minValue : null,
18257     /**
18258      * @cfg {Date/String} maxValue
18259      * The maximum allowed date. Can be either a Javascript date object or a string date in a
18260      * valid format (defaults to null).
18261      */
18262     maxValue : null,
18263     /**
18264      * @cfg {String} minText
18265      * The error text to display when the date in the cell is before minValue (defaults to
18266      * 'The date in this field must be after {minValue}').
18267      */
18268     minText : "The date in this field must be equal to or after {0}",
18269     /**
18270      * @cfg {String} maxTextf
18271      * The error text to display when the date in the cell is after maxValue (defaults to
18272      * 'The date in this field must be before {maxValue}').
18273      */
18274     maxText : "The date in this field must be equal to or before {0}",
18275     /**
18276      * @cfg {String} invalidText
18277      * The error text to display when the date in the field is invalid (defaults to
18278      * '{value} is not a valid date - it must be in the format {format}').
18279      */
18280     invalidText : "{0} is not a valid date - it must be in the format {1}",
18281     /**
18282      * @cfg {String} triggerClass
18283      * An additional CSS class used to style the trigger button.  The trigger will always get the
18284      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
18285      * which displays a calendar icon).
18286      */
18287     triggerClass : 'x-form-date-trigger',
18288     
18289
18290     /**
18291      * @cfg {Boolean} useIso
18292      * if enabled, then the date field will use a hidden field to store the 
18293      * real value as iso formated date. default (true)
18294      */ 
18295     useIso : true,
18296     /**
18297      * @cfg {String/Object} autoCreate
18298      * A DomHelper element spec, or true for a default element spec (defaults to
18299      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
18300      */ 
18301     // private
18302     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
18303     
18304     // private
18305     hiddenField: false,
18306     
18307     hideMonthPicker : false,
18308     
18309     onRender : function(ct, position)
18310     {
18311         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
18312         if (this.useIso) {
18313             this.el.dom.removeAttribute('name'); 
18314             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
18315                     'before', true);
18316             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
18317             // prevent input submission
18318             this.hiddenName = this.name;
18319         }
18320             
18321             
18322     },
18323     
18324     // private
18325     validateValue : function(value)
18326     {
18327         value = this.formatDate(value);
18328         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
18329             return false;
18330         }
18331         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
18332              return true;
18333         }
18334         var svalue = value;
18335         value = this.parseDate(value);
18336         if(!value){
18337             this.markInvalid(String.format(this.invalidText, svalue, this.format));
18338             return false;
18339         }
18340         var time = value.getTime();
18341         if(this.minValue && time < this.minValue.getTime()){
18342             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
18343             return false;
18344         }
18345         if(this.maxValue && time > this.maxValue.getTime()){
18346             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
18347             return false;
18348         }
18349         /*if(this.disabledDays){
18350             var day = value.getDay();
18351             for(var i = 0; i < this.disabledDays.length; i++) {
18352                 if(day === this.disabledDays[i]){
18353                     this.markInvalid(this.disabledDaysText);
18354                     return false;
18355                 }
18356             }
18357         }
18358         */
18359         var fvalue = this.formatDate(value);
18360         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
18361             this.markInvalid(String.format(this.disabledDatesText, fvalue));
18362             return false;
18363         }
18364         */
18365         return true;
18366     },
18367
18368     // private
18369     // Provides logic to override the default TriggerField.validateBlur which just returns true
18370     validateBlur : function(){
18371         return !this.menu || !this.menu.isVisible();
18372     },
18373
18374     /**
18375      * Returns the current date value of the date field.
18376      * @return {Date} The date value
18377      */
18378     getValue : function(){
18379         
18380         
18381         
18382         return  this.hiddenField ?
18383                 this.hiddenField.value :
18384                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
18385     },
18386
18387     /**
18388      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
18389      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
18390      * (the default format used is "m/d/y").
18391      * <br />Usage:
18392      * <pre><code>
18393 //All of these calls set the same date value (May 4, 2006)
18394
18395 //Pass a date object:
18396 var dt = new Date('5/4/06');
18397 monthField.setValue(dt);
18398
18399 //Pass a date string (default format):
18400 monthField.setValue('5/4/06');
18401
18402 //Pass a date string (custom format):
18403 monthField.format = 'Y-m-d';
18404 monthField.setValue('2006-5-4');
18405 </code></pre>
18406      * @param {String/Date} date The date or valid date string
18407      */
18408     setValue : function(date){
18409         Roo.log('month setValue' + date);
18410         // can only be first of month..
18411         
18412         var val = this.parseDate(date);
18413         
18414         if (this.hiddenField) {
18415             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
18416         }
18417         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
18418         this.value = this.parseDate(date);
18419     },
18420
18421     // private
18422     parseDate : function(value){
18423         if(!value || value instanceof Date){
18424             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
18425             return value;
18426         }
18427         var v = Date.parseDate(value, this.format);
18428         if (!v && this.useIso) {
18429             v = Date.parseDate(value, 'Y-m-d');
18430         }
18431         if (v) {
18432             // 
18433             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
18434         }
18435         
18436         
18437         if(!v && this.altFormats){
18438             if(!this.altFormatsArray){
18439                 this.altFormatsArray = this.altFormats.split("|");
18440             }
18441             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18442                 v = Date.parseDate(value, this.altFormatsArray[i]);
18443             }
18444         }
18445         return v;
18446     },
18447
18448     // private
18449     formatDate : function(date, fmt){
18450         return (!date || !(date instanceof Date)) ?
18451                date : date.dateFormat(fmt || this.format);
18452     },
18453
18454     // private
18455     menuListeners : {
18456         select: function(m, d){
18457             this.setValue(d);
18458             this.fireEvent('select', this, d);
18459         },
18460         show : function(){ // retain focus styling
18461             this.onFocus();
18462         },
18463         hide : function(){
18464             this.focus.defer(10, this);
18465             var ml = this.menuListeners;
18466             this.menu.un("select", ml.select,  this);
18467             this.menu.un("show", ml.show,  this);
18468             this.menu.un("hide", ml.hide,  this);
18469         }
18470     },
18471     // private
18472     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
18473     onTriggerClick : function(){
18474         if(this.disabled){
18475             return;
18476         }
18477         if(this.menu == null){
18478             this.menu = new Roo.menu.DateMenu();
18479            
18480         }
18481         
18482         Roo.apply(this.menu.picker,  {
18483             
18484             showClear: this.allowBlank,
18485             minDate : this.minValue,
18486             maxDate : this.maxValue,
18487             disabledDatesRE : this.ddMatch,
18488             disabledDatesText : this.disabledDatesText,
18489             
18490             format : this.useIso ? 'Y-m-d' : this.format,
18491             minText : String.format(this.minText, this.formatDate(this.minValue)),
18492             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
18493             
18494         });
18495          this.menu.on(Roo.apply({}, this.menuListeners, {
18496             scope:this
18497         }));
18498        
18499         
18500         var m = this.menu;
18501         var p = m.picker;
18502         
18503         // hide month picker get's called when we called by 'before hide';
18504         
18505         var ignorehide = true;
18506         p.hideMonthPicker  = function(disableAnim){
18507             if (ignorehide) {
18508                 return;
18509             }
18510              if(this.monthPicker){
18511                 Roo.log("hideMonthPicker called");
18512                 if(disableAnim === true){
18513                     this.monthPicker.hide();
18514                 }else{
18515                     this.monthPicker.slideOut('t', {duration:.2});
18516                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
18517                     p.fireEvent("select", this, this.value);
18518                     m.hide();
18519                 }
18520             }
18521         }
18522         
18523         Roo.log('picker set value');
18524         Roo.log(this.getValue());
18525         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
18526         m.show(this.el, 'tl-bl?');
18527         ignorehide  = false;
18528         // this will trigger hideMonthPicker..
18529         
18530         
18531         // hidden the day picker
18532         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
18533         
18534         
18535         
18536       
18537         
18538         p.showMonthPicker.defer(100, p);
18539     
18540         
18541        
18542     },
18543
18544     beforeBlur : function(){
18545         var v = this.parseDate(this.getRawValue());
18546         if(v){
18547             this.setValue(v);
18548         }
18549     }
18550
18551     /** @cfg {Boolean} grow @hide */
18552     /** @cfg {Number} growMin @hide */
18553     /** @cfg {Number} growMax @hide */
18554     /**
18555      * @hide
18556      * @method autoSize
18557      */
18558 });/*
18559  * Based on:
18560  * Ext JS Library 1.1.1
18561  * Copyright(c) 2006-2007, Ext JS, LLC.
18562  *
18563  * Originally Released Under LGPL - original licence link has changed is not relivant.
18564  *
18565  * Fork - LGPL
18566  * <script type="text/javascript">
18567  */
18568  
18569
18570 /**
18571  * @class Roo.form.ComboBox
18572  * @extends Roo.form.TriggerField
18573  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
18574  * @constructor
18575  * Create a new ComboBox.
18576  * @param {Object} config Configuration options
18577  */
18578 Roo.form.ComboBox = function(config){
18579     Roo.form.ComboBox.superclass.constructor.call(this, config);
18580     this.addEvents({
18581         /**
18582          * @event expand
18583          * Fires when the dropdown list is expanded
18584              * @param {Roo.form.ComboBox} combo This combo box
18585              */
18586         'expand' : true,
18587         /**
18588          * @event collapse
18589          * Fires when the dropdown list is collapsed
18590              * @param {Roo.form.ComboBox} combo This combo box
18591              */
18592         'collapse' : true,
18593         /**
18594          * @event beforeselect
18595          * Fires before a list item is selected. Return false to cancel the selection.
18596              * @param {Roo.form.ComboBox} combo This combo box
18597              * @param {Roo.data.Record} record The data record returned from the underlying store
18598              * @param {Number} index The index of the selected item in the dropdown list
18599              */
18600         'beforeselect' : true,
18601         /**
18602          * @event select
18603          * Fires when a list item is selected
18604              * @param {Roo.form.ComboBox} combo This combo box
18605              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
18606              * @param {Number} index The index of the selected item in the dropdown list
18607              */
18608         'select' : true,
18609         /**
18610          * @event beforequery
18611          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
18612          * The event object passed has these properties:
18613              * @param {Roo.form.ComboBox} combo This combo box
18614              * @param {String} query The query
18615              * @param {Boolean} forceAll true to force "all" query
18616              * @param {Boolean} cancel true to cancel the query
18617              * @param {Object} e The query event object
18618              */
18619         'beforequery': true,
18620          /**
18621          * @event add
18622          * Fires when the 'add' icon is pressed (add a listener to enable add button)
18623              * @param {Roo.form.ComboBox} combo This combo box
18624              */
18625         'add' : true,
18626         /**
18627          * @event edit
18628          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
18629              * @param {Roo.form.ComboBox} combo This combo box
18630              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
18631              */
18632         'edit' : true
18633         
18634         
18635     });
18636     if(this.transform){
18637         this.allowDomMove = false;
18638         var s = Roo.getDom(this.transform);
18639         if(!this.hiddenName){
18640             this.hiddenName = s.name;
18641         }
18642         if(!this.store){
18643             this.mode = 'local';
18644             var d = [], opts = s.options;
18645             for(var i = 0, len = opts.length;i < len; i++){
18646                 var o = opts[i];
18647                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
18648                 if(o.selected) {
18649                     this.value = value;
18650                 }
18651                 d.push([value, o.text]);
18652             }
18653             this.store = new Roo.data.SimpleStore({
18654                 'id': 0,
18655                 fields: ['value', 'text'],
18656                 data : d
18657             });
18658             this.valueField = 'value';
18659             this.displayField = 'text';
18660         }
18661         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
18662         if(!this.lazyRender){
18663             this.target = true;
18664             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
18665             s.parentNode.removeChild(s); // remove it
18666             this.render(this.el.parentNode);
18667         }else{
18668             s.parentNode.removeChild(s); // remove it
18669         }
18670
18671     }
18672     if (this.store) {
18673         this.store = Roo.factory(this.store, Roo.data);
18674     }
18675     
18676     this.selectedIndex = -1;
18677     if(this.mode == 'local'){
18678         if(config.queryDelay === undefined){
18679             this.queryDelay = 10;
18680         }
18681         if(config.minChars === undefined){
18682             this.minChars = 0;
18683         }
18684     }
18685 };
18686
18687 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
18688     /**
18689      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
18690      */
18691     /**
18692      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
18693      * rendering into an Roo.Editor, defaults to false)
18694      */
18695     /**
18696      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
18697      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
18698      */
18699     /**
18700      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
18701      */
18702     /**
18703      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
18704      * the dropdown list (defaults to undefined, with no header element)
18705      */
18706
18707      /**
18708      * @cfg {String/Roo.Template} tpl The template to use to render the output
18709      */
18710      
18711     // private
18712     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
18713     /**
18714      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
18715      */
18716     listWidth: undefined,
18717     /**
18718      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
18719      * mode = 'remote' or 'text' if mode = 'local')
18720      */
18721     displayField: undefined,
18722     /**
18723      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
18724      * mode = 'remote' or 'value' if mode = 'local'). 
18725      * Note: use of a valueField requires the user make a selection
18726      * in order for a value to be mapped.
18727      */
18728     valueField: undefined,
18729     
18730     
18731     /**
18732      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
18733      * field's data value (defaults to the underlying DOM element's name)
18734      */
18735     hiddenName: undefined,
18736     /**
18737      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
18738      */
18739     listClass: '',
18740     /**
18741      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
18742      */
18743     selectedClass: 'x-combo-selected',
18744     /**
18745      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
18746      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
18747      * which displays a downward arrow icon).
18748      */
18749     triggerClass : 'x-form-arrow-trigger',
18750     /**
18751      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
18752      */
18753     shadow:'sides',
18754     /**
18755      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
18756      * anchor positions (defaults to 'tl-bl')
18757      */
18758     listAlign: 'tl-bl?',
18759     /**
18760      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
18761      */
18762     maxHeight: 300,
18763     /**
18764      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
18765      * query specified by the allQuery config option (defaults to 'query')
18766      */
18767     triggerAction: 'query',
18768     /**
18769      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
18770      * (defaults to 4, does not apply if editable = false)
18771      */
18772     minChars : 4,
18773     /**
18774      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
18775      * delay (typeAheadDelay) if it matches a known value (defaults to false)
18776      */
18777     typeAhead: false,
18778     /**
18779      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
18780      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
18781      */
18782     queryDelay: 500,
18783     /**
18784      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
18785      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
18786      */
18787     pageSize: 0,
18788     /**
18789      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
18790      * when editable = true (defaults to false)
18791      */
18792     selectOnFocus:false,
18793     /**
18794      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
18795      */
18796     queryParam: 'query',
18797     /**
18798      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
18799      * when mode = 'remote' (defaults to 'Loading...')
18800      */
18801     loadingText: 'Loading...',
18802     /**
18803      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
18804      */
18805     resizable: false,
18806     /**
18807      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
18808      */
18809     handleHeight : 8,
18810     /**
18811      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
18812      * traditional select (defaults to true)
18813      */
18814     editable: true,
18815     /**
18816      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
18817      */
18818     allQuery: '',
18819     /**
18820      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
18821      */
18822     mode: 'remote',
18823     /**
18824      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
18825      * listWidth has a higher value)
18826      */
18827     minListWidth : 70,
18828     /**
18829      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
18830      * allow the user to set arbitrary text into the field (defaults to false)
18831      */
18832     forceSelection:false,
18833     /**
18834      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
18835      * if typeAhead = true (defaults to 250)
18836      */
18837     typeAheadDelay : 250,
18838     /**
18839      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
18840      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
18841      */
18842     valueNotFoundText : undefined,
18843     /**
18844      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
18845      */
18846     blockFocus : false,
18847     
18848     /**
18849      * @cfg {Boolean} disableClear Disable showing of clear button.
18850      */
18851     disableClear : false,
18852     /**
18853      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
18854      */
18855     alwaysQuery : false,
18856     
18857     //private
18858     addicon : false,
18859     editicon: false,
18860     
18861     // element that contains real text value.. (when hidden is used..)
18862      
18863     // private
18864     onRender : function(ct, position){
18865         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
18866         if(this.hiddenName){
18867             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
18868                     'before', true);
18869             this.hiddenField.value =
18870                 this.hiddenValue !== undefined ? this.hiddenValue :
18871                 this.value !== undefined ? this.value : '';
18872
18873             // prevent input submission
18874             this.el.dom.removeAttribute('name');
18875              
18876              
18877         }
18878         if(Roo.isGecko){
18879             this.el.dom.setAttribute('autocomplete', 'off');
18880         }
18881
18882         var cls = 'x-combo-list';
18883
18884         this.list = new Roo.Layer({
18885             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
18886         });
18887
18888         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
18889         this.list.setWidth(lw);
18890         this.list.swallowEvent('mousewheel');
18891         this.assetHeight = 0;
18892
18893         if(this.title){
18894             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
18895             this.assetHeight += this.header.getHeight();
18896         }
18897
18898         this.innerList = this.list.createChild({cls:cls+'-inner'});
18899         this.innerList.on('mouseover', this.onViewOver, this);
18900         this.innerList.on('mousemove', this.onViewMove, this);
18901         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18902         
18903         if(this.allowBlank && !this.pageSize && !this.disableClear){
18904             this.footer = this.list.createChild({cls:cls+'-ft'});
18905             this.pageTb = new Roo.Toolbar(this.footer);
18906            
18907         }
18908         if(this.pageSize){
18909             this.footer = this.list.createChild({cls:cls+'-ft'});
18910             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
18911                     {pageSize: this.pageSize});
18912             
18913         }
18914         
18915         if (this.pageTb && this.allowBlank && !this.disableClear) {
18916             var _this = this;
18917             this.pageTb.add(new Roo.Toolbar.Fill(), {
18918                 cls: 'x-btn-icon x-btn-clear',
18919                 text: '&#160;',
18920                 handler: function()
18921                 {
18922                     _this.collapse();
18923                     _this.clearValue();
18924                     _this.onSelect(false, -1);
18925                 }
18926             });
18927         }
18928         if (this.footer) {
18929             this.assetHeight += this.footer.getHeight();
18930         }
18931         
18932
18933         if(!this.tpl){
18934             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
18935         }
18936
18937         this.view = new Roo.View(this.innerList, this.tpl, {
18938             singleSelect:true, store: this.store, selectedClass: this.selectedClass
18939         });
18940
18941         this.view.on('click', this.onViewClick, this);
18942
18943         this.store.on('beforeload', this.onBeforeLoad, this);
18944         this.store.on('load', this.onLoad, this);
18945         this.store.on('loadexception', this.onLoadException, this);
18946
18947         if(this.resizable){
18948             this.resizer = new Roo.Resizable(this.list,  {
18949                pinned:true, handles:'se'
18950             });
18951             this.resizer.on('resize', function(r, w, h){
18952                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
18953                 this.listWidth = w;
18954                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
18955                 this.restrictHeight();
18956             }, this);
18957             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
18958         }
18959         if(!this.editable){
18960             this.editable = true;
18961             this.setEditable(false);
18962         }  
18963         
18964         
18965         if (typeof(this.events.add.listeners) != 'undefined') {
18966             
18967             this.addicon = this.wrap.createChild(
18968                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
18969        
18970             this.addicon.on('click', function(e) {
18971                 this.fireEvent('add', this);
18972             }, this);
18973         }
18974         if (typeof(this.events.edit.listeners) != 'undefined') {
18975             
18976             this.editicon = this.wrap.createChild(
18977                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
18978             if (this.addicon) {
18979                 this.editicon.setStyle('margin-left', '40px');
18980             }
18981             this.editicon.on('click', function(e) {
18982                 
18983                 // we fire even  if inothing is selected..
18984                 this.fireEvent('edit', this, this.lastData );
18985                 
18986             }, this);
18987         }
18988         
18989         
18990         
18991     },
18992
18993     // private
18994     initEvents : function(){
18995         Roo.form.ComboBox.superclass.initEvents.call(this);
18996
18997         this.keyNav = new Roo.KeyNav(this.el, {
18998             "up" : function(e){
18999                 this.inKeyMode = true;
19000                 this.selectPrev();
19001             },
19002
19003             "down" : function(e){
19004                 if(!this.isExpanded()){
19005                     this.onTriggerClick();
19006                 }else{
19007                     this.inKeyMode = true;
19008                     this.selectNext();
19009                 }
19010             },
19011
19012             "enter" : function(e){
19013                 this.onViewClick();
19014                 //return true;
19015             },
19016
19017             "esc" : function(e){
19018                 this.collapse();
19019             },
19020
19021             "tab" : function(e){
19022                 this.onViewClick(false);
19023                 this.fireEvent("specialkey", this, e);
19024                 return true;
19025             },
19026
19027             scope : this,
19028
19029             doRelay : function(foo, bar, hname){
19030                 if(hname == 'down' || this.scope.isExpanded()){
19031                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
19032                 }
19033                 return true;
19034             },
19035
19036             forceKeyDown: true
19037         });
19038         this.queryDelay = Math.max(this.queryDelay || 10,
19039                 this.mode == 'local' ? 10 : 250);
19040         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
19041         if(this.typeAhead){
19042             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
19043         }
19044         if(this.editable !== false){
19045             this.el.on("keyup", this.onKeyUp, this);
19046         }
19047         if(this.forceSelection){
19048             this.on('blur', this.doForce, this);
19049         }
19050     },
19051
19052     onDestroy : function(){
19053         if(this.view){
19054             this.view.setStore(null);
19055             this.view.el.removeAllListeners();
19056             this.view.el.remove();
19057             this.view.purgeListeners();
19058         }
19059         if(this.list){
19060             this.list.destroy();
19061         }
19062         if(this.store){
19063             this.store.un('beforeload', this.onBeforeLoad, this);
19064             this.store.un('load', this.onLoad, this);
19065             this.store.un('loadexception', this.onLoadException, this);
19066         }
19067         Roo.form.ComboBox.superclass.onDestroy.call(this);
19068     },
19069
19070     // private
19071     fireKey : function(e){
19072         if(e.isNavKeyPress() && !this.list.isVisible()){
19073             this.fireEvent("specialkey", this, e);
19074         }
19075     },
19076
19077     // private
19078     onResize: function(w, h){
19079         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
19080         
19081         if(typeof w != 'number'){
19082             // we do not handle it!?!?
19083             return;
19084         }
19085         var tw = this.trigger.getWidth();
19086         tw += this.addicon ? this.addicon.getWidth() : 0;
19087         tw += this.editicon ? this.editicon.getWidth() : 0;
19088         var x = w - tw;
19089         this.el.setWidth( this.adjustWidth('input', x));
19090             
19091         this.trigger.setStyle('left', x+'px');
19092         
19093         if(this.list && this.listWidth === undefined){
19094             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
19095             this.list.setWidth(lw);
19096             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19097         }
19098         
19099     
19100         
19101     },
19102
19103     /**
19104      * Allow or prevent the user from directly editing the field text.  If false is passed,
19105      * the user will only be able to select from the items defined in the dropdown list.  This method
19106      * is the runtime equivalent of setting the 'editable' config option at config time.
19107      * @param {Boolean} value True to allow the user to directly edit the field text
19108      */
19109     setEditable : function(value){
19110         if(value == this.editable){
19111             return;
19112         }
19113         this.editable = value;
19114         if(!value){
19115             this.el.dom.setAttribute('readOnly', true);
19116             this.el.on('mousedown', this.onTriggerClick,  this);
19117             this.el.addClass('x-combo-noedit');
19118         }else{
19119             this.el.dom.setAttribute('readOnly', false);
19120             this.el.un('mousedown', this.onTriggerClick,  this);
19121             this.el.removeClass('x-combo-noedit');
19122         }
19123     },
19124
19125     // private
19126     onBeforeLoad : function(){
19127         if(!this.hasFocus){
19128             return;
19129         }
19130         this.innerList.update(this.loadingText ?
19131                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
19132         this.restrictHeight();
19133         this.selectedIndex = -1;
19134     },
19135
19136     // private
19137     onLoad : function(){
19138         if(!this.hasFocus){
19139             return;
19140         }
19141         if(this.store.getCount() > 0){
19142             this.expand();
19143             this.restrictHeight();
19144             if(this.lastQuery == this.allQuery){
19145                 if(this.editable){
19146                     this.el.dom.select();
19147                 }
19148                 if(!this.selectByValue(this.value, true)){
19149                     this.select(0, true);
19150                 }
19151             }else{
19152                 this.selectNext();
19153                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
19154                     this.taTask.delay(this.typeAheadDelay);
19155                 }
19156             }
19157         }else{
19158             this.onEmptyResults();
19159         }
19160         //this.el.focus();
19161     },
19162     // private
19163     onLoadException : function()
19164     {
19165         this.collapse();
19166         Roo.log(this.store.reader.jsonData);
19167         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
19168             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
19169         }
19170         
19171         
19172     },
19173     // private
19174     onTypeAhead : function(){
19175         if(this.store.getCount() > 0){
19176             var r = this.store.getAt(0);
19177             var newValue = r.data[this.displayField];
19178             var len = newValue.length;
19179             var selStart = this.getRawValue().length;
19180             if(selStart != len){
19181                 this.setRawValue(newValue);
19182                 this.selectText(selStart, newValue.length);
19183             }
19184         }
19185     },
19186
19187     // private
19188     onSelect : function(record, index){
19189         if(this.fireEvent('beforeselect', this, record, index) !== false){
19190             this.setFromData(index > -1 ? record.data : false);
19191             this.collapse();
19192             this.fireEvent('select', this, record, index);
19193         }
19194     },
19195
19196     /**
19197      * Returns the currently selected field value or empty string if no value is set.
19198      * @return {String} value The selected value
19199      */
19200     getValue : function(){
19201         if(this.valueField){
19202             return typeof this.value != 'undefined' ? this.value : '';
19203         }
19204         return Roo.form.ComboBox.superclass.getValue.call(this);
19205     },
19206
19207     /**
19208      * Clears any text/value currently set in the field
19209      */
19210     clearValue : function(){
19211         if(this.hiddenField){
19212             this.hiddenField.value = '';
19213         }
19214         this.value = '';
19215         this.setRawValue('');
19216         this.lastSelectionText = '';
19217         
19218     },
19219
19220     /**
19221      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
19222      * will be displayed in the field.  If the value does not match the data value of an existing item,
19223      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
19224      * Otherwise the field will be blank (although the value will still be set).
19225      * @param {String} value The value to match
19226      */
19227     setValue : function(v){
19228         var text = v;
19229         if(this.valueField){
19230             var r = this.findRecord(this.valueField, v);
19231             if(r){
19232                 text = r.data[this.displayField];
19233             }else if(this.valueNotFoundText !== undefined){
19234                 text = this.valueNotFoundText;
19235             }
19236         }
19237         this.lastSelectionText = text;
19238         if(this.hiddenField){
19239             this.hiddenField.value = v;
19240         }
19241         Roo.form.ComboBox.superclass.setValue.call(this, text);
19242         this.value = v;
19243     },
19244     /**
19245      * @property {Object} the last set data for the element
19246      */
19247     
19248     lastData : false,
19249     /**
19250      * Sets the value of the field based on a object which is related to the record format for the store.
19251      * @param {Object} value the value to set as. or false on reset?
19252      */
19253     setFromData : function(o){
19254         var dv = ''; // display value
19255         var vv = ''; // value value..
19256         this.lastData = o;
19257         if (this.displayField) {
19258             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
19259         } else {
19260             // this is an error condition!!!
19261             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
19262         }
19263         
19264         if(this.valueField){
19265             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
19266         }
19267         if(this.hiddenField){
19268             this.hiddenField.value = vv;
19269             
19270             this.lastSelectionText = dv;
19271             Roo.form.ComboBox.superclass.setValue.call(this, dv);
19272             this.value = vv;
19273             return;
19274         }
19275         // no hidden field.. - we store the value in 'value', but still display
19276         // display field!!!!
19277         this.lastSelectionText = dv;
19278         Roo.form.ComboBox.superclass.setValue.call(this, dv);
19279         this.value = vv;
19280         
19281         
19282     },
19283     // private
19284     reset : function(){
19285         // overridden so that last data is reset..
19286         this.setValue(this.resetValue);
19287         this.originalValue = this.getValue();
19288         this.clearInvalid();
19289         this.lastData = false;
19290         if (this.view) {
19291             this.view.clearSelections();
19292         }
19293     },
19294     // private
19295     findRecord : function(prop, value){
19296         var record;
19297         if(this.store.getCount() > 0){
19298             this.store.each(function(r){
19299                 if(r.data[prop] == value){
19300                     record = r;
19301                     return false;
19302                 }
19303                 return true;
19304             });
19305         }
19306         return record;
19307     },
19308     
19309     getName: function()
19310     {
19311         // returns hidden if it's set..
19312         if (!this.rendered) {return ''};
19313         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
19314         
19315     },
19316     // private
19317     onViewMove : function(e, t){
19318         this.inKeyMode = false;
19319     },
19320
19321     // private
19322     onViewOver : function(e, t){
19323         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
19324             return;
19325         }
19326         var item = this.view.findItemFromChild(t);
19327         if(item){
19328             var index = this.view.indexOf(item);
19329             this.select(index, false);
19330         }
19331     },
19332
19333     // private
19334     onViewClick : function(doFocus)
19335     {
19336         var index = this.view.getSelectedIndexes()[0];
19337         var r = this.store.getAt(index);
19338         if(r){
19339             this.onSelect(r, index);
19340         }
19341         if(doFocus !== false && !this.blockFocus){
19342             this.el.focus();
19343         }
19344     },
19345
19346     // private
19347     restrictHeight : function(){
19348         this.innerList.dom.style.height = '';
19349         var inner = this.innerList.dom;
19350         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
19351         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
19352         this.list.beginUpdate();
19353         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
19354         this.list.alignTo(this.el, this.listAlign);
19355         this.list.endUpdate();
19356     },
19357
19358     // private
19359     onEmptyResults : function(){
19360         this.collapse();
19361     },
19362
19363     /**
19364      * Returns true if the dropdown list is expanded, else false.
19365      */
19366     isExpanded : function(){
19367         return this.list.isVisible();
19368     },
19369
19370     /**
19371      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
19372      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19373      * @param {String} value The data value of the item to select
19374      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19375      * selected item if it is not currently in view (defaults to true)
19376      * @return {Boolean} True if the value matched an item in the list, else false
19377      */
19378     selectByValue : function(v, scrollIntoView){
19379         if(v !== undefined && v !== null){
19380             var r = this.findRecord(this.valueField || this.displayField, v);
19381             if(r){
19382                 this.select(this.store.indexOf(r), scrollIntoView);
19383                 return true;
19384             }
19385         }
19386         return false;
19387     },
19388
19389     /**
19390      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
19391      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19392      * @param {Number} index The zero-based index of the list item to select
19393      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19394      * selected item if it is not currently in view (defaults to true)
19395      */
19396     select : function(index, scrollIntoView){
19397         this.selectedIndex = index;
19398         this.view.select(index);
19399         if(scrollIntoView !== false){
19400             var el = this.view.getNode(index);
19401             if(el){
19402                 this.innerList.scrollChildIntoView(el, false);
19403             }
19404         }
19405     },
19406
19407     // private
19408     selectNext : function(){
19409         var ct = this.store.getCount();
19410         if(ct > 0){
19411             if(this.selectedIndex == -1){
19412                 this.select(0);
19413             }else if(this.selectedIndex < ct-1){
19414                 this.select(this.selectedIndex+1);
19415             }
19416         }
19417     },
19418
19419     // private
19420     selectPrev : function(){
19421         var ct = this.store.getCount();
19422         if(ct > 0){
19423             if(this.selectedIndex == -1){
19424                 this.select(0);
19425             }else if(this.selectedIndex != 0){
19426                 this.select(this.selectedIndex-1);
19427             }
19428         }
19429     },
19430
19431     // private
19432     onKeyUp : function(e){
19433         if(this.editable !== false && !e.isSpecialKey()){
19434             this.lastKey = e.getKey();
19435             this.dqTask.delay(this.queryDelay);
19436         }
19437     },
19438
19439     // private
19440     validateBlur : function(){
19441         return !this.list || !this.list.isVisible();   
19442     },
19443
19444     // private
19445     initQuery : function(){
19446         this.doQuery(this.getRawValue());
19447     },
19448
19449     // private
19450     doForce : function(){
19451         if(this.el.dom.value.length > 0){
19452             this.el.dom.value =
19453                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
19454              
19455         }
19456     },
19457
19458     /**
19459      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
19460      * query allowing the query action to be canceled if needed.
19461      * @param {String} query The SQL query to execute
19462      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
19463      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
19464      * saved in the current store (defaults to false)
19465      */
19466     doQuery : function(q, forceAll){
19467         if(q === undefined || q === null){
19468             q = '';
19469         }
19470         var qe = {
19471             query: q,
19472             forceAll: forceAll,
19473             combo: this,
19474             cancel:false
19475         };
19476         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
19477             return false;
19478         }
19479         q = qe.query;
19480         forceAll = qe.forceAll;
19481         if(forceAll === true || (q.length >= this.minChars)){
19482             if(this.lastQuery != q || this.alwaysQuery){
19483                 this.lastQuery = q;
19484                 if(this.mode == 'local'){
19485                     this.selectedIndex = -1;
19486                     if(forceAll){
19487                         this.store.clearFilter();
19488                     }else{
19489                         this.store.filter(this.displayField, q);
19490                     }
19491                     this.onLoad();
19492                 }else{
19493                     this.store.baseParams[this.queryParam] = q;
19494                     this.store.load({
19495                         params: this.getParams(q)
19496                     });
19497                     this.expand();
19498                 }
19499             }else{
19500                 this.selectedIndex = -1;
19501                 this.onLoad();   
19502             }
19503         }
19504     },
19505
19506     // private
19507     getParams : function(q){
19508         var p = {};
19509         //p[this.queryParam] = q;
19510         if(this.pageSize){
19511             p.start = 0;
19512             p.limit = this.pageSize;
19513         }
19514         return p;
19515     },
19516
19517     /**
19518      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
19519      */
19520     collapse : function(){
19521         if(!this.isExpanded()){
19522             return;
19523         }
19524         this.list.hide();
19525         Roo.get(document).un('mousedown', this.collapseIf, this);
19526         Roo.get(document).un('mousewheel', this.collapseIf, this);
19527         if (!this.editable) {
19528             Roo.get(document).un('keydown', this.listKeyPress, this);
19529         }
19530         this.fireEvent('collapse', this);
19531     },
19532
19533     // private
19534     collapseIf : function(e){
19535         if(!e.within(this.wrap) && !e.within(this.list)){
19536             this.collapse();
19537         }
19538     },
19539
19540     /**
19541      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
19542      */
19543     expand : function(){
19544         if(this.isExpanded() || !this.hasFocus){
19545             return;
19546         }
19547         this.list.alignTo(this.el, this.listAlign);
19548         this.list.show();
19549         Roo.get(document).on('mousedown', this.collapseIf, this);
19550         Roo.get(document).on('mousewheel', this.collapseIf, this);
19551         if (!this.editable) {
19552             Roo.get(document).on('keydown', this.listKeyPress, this);
19553         }
19554         
19555         this.fireEvent('expand', this);
19556     },
19557
19558     // private
19559     // Implements the default empty TriggerField.onTriggerClick function
19560     onTriggerClick : function(){
19561         if(this.disabled){
19562             return;
19563         }
19564         if(this.isExpanded()){
19565             this.collapse();
19566             if (!this.blockFocus) {
19567                 this.el.focus();
19568             }
19569             
19570         }else {
19571             this.hasFocus = true;
19572             if(this.triggerAction == 'all') {
19573                 this.doQuery(this.allQuery, true);
19574             } else {
19575                 this.doQuery(this.getRawValue());
19576             }
19577             if (!this.blockFocus) {
19578                 this.el.focus();
19579             }
19580         }
19581     },
19582     listKeyPress : function(e)
19583     {
19584         //Roo.log('listkeypress');
19585         // scroll to first matching element based on key pres..
19586         if (e.isSpecialKey()) {
19587             return false;
19588         }
19589         var k = String.fromCharCode(e.getKey()).toUpperCase();
19590         //Roo.log(k);
19591         var match  = false;
19592         var csel = this.view.getSelectedNodes();
19593         var cselitem = false;
19594         if (csel.length) {
19595             var ix = this.view.indexOf(csel[0]);
19596             cselitem  = this.store.getAt(ix);
19597             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
19598                 cselitem = false;
19599             }
19600             
19601         }
19602         
19603         this.store.each(function(v) { 
19604             if (cselitem) {
19605                 // start at existing selection.
19606                 if (cselitem.id == v.id) {
19607                     cselitem = false;
19608                 }
19609                 return;
19610             }
19611                 
19612             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
19613                 match = this.store.indexOf(v);
19614                 return false;
19615             }
19616         }, this);
19617         
19618         if (match === false) {
19619             return true; // no more action?
19620         }
19621         // scroll to?
19622         this.view.select(match);
19623         var sn = Roo.get(this.view.getSelectedNodes()[0]);
19624         sn.scrollIntoView(sn.dom.parentNode, false);
19625     }
19626
19627     /** 
19628     * @cfg {Boolean} grow 
19629     * @hide 
19630     */
19631     /** 
19632     * @cfg {Number} growMin 
19633     * @hide 
19634     */
19635     /** 
19636     * @cfg {Number} growMax 
19637     * @hide 
19638     */
19639     /**
19640      * @hide
19641      * @method autoSize
19642      */
19643 });/*
19644  * Copyright(c) 2010-2012, Roo J Solutions Limited
19645  *
19646  * Licence LGPL
19647  *
19648  */
19649
19650 /**
19651  * @class Roo.form.ComboBoxArray
19652  * @extends Roo.form.TextField
19653  * A facebook style adder... for lists of email / people / countries  etc...
19654  * pick multiple items from a combo box, and shows each one.
19655  *
19656  *  Fred [x]  Brian [x]  [Pick another |v]
19657  *
19658  *
19659  *  For this to work: it needs various extra information
19660  *    - normal combo problay has
19661  *      name, hiddenName
19662  *    + displayField, valueField
19663  *
19664  *    For our purpose...
19665  *
19666  *
19667  *   If we change from 'extends' to wrapping...
19668  *   
19669  *  
19670  *
19671  
19672  
19673  * @constructor
19674  * Create a new ComboBoxArray.
19675  * @param {Object} config Configuration options
19676  */
19677  
19678
19679 Roo.form.ComboBoxArray = function(config)
19680 {
19681     this.addEvents({
19682         /**
19683          * @event beforeremove
19684          * Fires before remove the value from the list
19685              * @param {Roo.form.ComboBoxArray} _self This combo box array
19686              * @param {Roo.form.ComboBoxArray.Item} item removed item
19687              */
19688         'beforeremove' : true,
19689         /**
19690          * @event remove
19691          * Fires when remove the value from the list
19692              * @param {Roo.form.ComboBoxArray} _self This combo box array
19693              * @param {Roo.form.ComboBoxArray.Item} item removed item
19694              */
19695         'remove' : true
19696         
19697         
19698     });
19699     
19700     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
19701     
19702     this.items = new Roo.util.MixedCollection(false);
19703     
19704     // construct the child combo...
19705     
19706     
19707     
19708     
19709    
19710     
19711 }
19712
19713  
19714 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
19715
19716     /**
19717      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
19718      */
19719     
19720     lastData : false,
19721     
19722     // behavies liek a hiddne field
19723     inputType:      'hidden',
19724     /**
19725      * @cfg {Number} width The width of the box that displays the selected element
19726      */ 
19727     width:          300,
19728
19729     
19730     
19731     /**
19732      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
19733      */
19734     name : false,
19735     /**
19736      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
19737      */
19738     hiddenName : false,
19739     
19740     
19741     // private the array of items that are displayed..
19742     items  : false,
19743     // private - the hidden field el.
19744     hiddenEl : false,
19745     // private - the filed el..
19746     el : false,
19747     
19748     //validateValue : function() { return true; }, // all values are ok!
19749     //onAddClick: function() { },
19750     
19751     onRender : function(ct, position) 
19752     {
19753         
19754         // create the standard hidden element
19755         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
19756         
19757         
19758         // give fake names to child combo;
19759         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
19760         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
19761         
19762         this.combo = Roo.factory(this.combo, Roo.form);
19763         this.combo.onRender(ct, position);
19764         if (typeof(this.combo.width) != 'undefined') {
19765             this.combo.onResize(this.combo.width,0);
19766         }
19767         
19768         this.combo.initEvents();
19769         
19770         // assigned so form know we need to do this..
19771         this.store          = this.combo.store;
19772         this.valueField     = this.combo.valueField;
19773         this.displayField   = this.combo.displayField ;
19774         
19775         
19776         this.combo.wrap.addClass('x-cbarray-grp');
19777         
19778         var cbwrap = this.combo.wrap.createChild(
19779             {tag: 'div', cls: 'x-cbarray-cb'},
19780             this.combo.el.dom
19781         );
19782         
19783              
19784         this.hiddenEl = this.combo.wrap.createChild({
19785             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
19786         });
19787         this.el = this.combo.wrap.createChild({
19788             tag: 'input',  type:'hidden' , name: this.name, value : ''
19789         });
19790          //   this.el.dom.removeAttribute("name");
19791         
19792         
19793         this.outerWrap = this.combo.wrap;
19794         this.wrap = cbwrap;
19795         
19796         this.outerWrap.setWidth(this.width);
19797         this.outerWrap.dom.removeChild(this.el.dom);
19798         
19799         this.wrap.dom.appendChild(this.el.dom);
19800         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
19801         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
19802         
19803         this.combo.trigger.setStyle('position','relative');
19804         this.combo.trigger.setStyle('left', '0px');
19805         this.combo.trigger.setStyle('top', '2px');
19806         
19807         this.combo.el.setStyle('vertical-align', 'text-bottom');
19808         
19809         //this.trigger.setStyle('vertical-align', 'top');
19810         
19811         // this should use the code from combo really... on('add' ....)
19812         if (this.adder) {
19813             
19814         
19815             this.adder = this.outerWrap.createChild(
19816                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
19817             var _t = this;
19818             this.adder.on('click', function(e) {
19819                 _t.fireEvent('adderclick', this, e);
19820             }, _t);
19821         }
19822         //var _t = this;
19823         //this.adder.on('click', this.onAddClick, _t);
19824         
19825         
19826         this.combo.on('select', function(cb, rec, ix) {
19827             this.addItem(rec.data);
19828             
19829             cb.setValue('');
19830             cb.el.dom.value = '';
19831             //cb.lastData = rec.data;
19832             // add to list
19833             
19834         }, this);
19835         
19836         
19837     },
19838     
19839     
19840     getName: function()
19841     {
19842         // returns hidden if it's set..
19843         if (!this.rendered) {return ''};
19844         return  this.hiddenName ? this.hiddenName : this.name;
19845         
19846     },
19847     
19848     
19849     onResize: function(w, h){
19850         
19851         return;
19852         // not sure if this is needed..
19853         //this.combo.onResize(w,h);
19854         
19855         if(typeof w != 'number'){
19856             // we do not handle it!?!?
19857             return;
19858         }
19859         var tw = this.combo.trigger.getWidth();
19860         tw += this.addicon ? this.addicon.getWidth() : 0;
19861         tw += this.editicon ? this.editicon.getWidth() : 0;
19862         var x = w - tw;
19863         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
19864             
19865         this.combo.trigger.setStyle('left', '0px');
19866         
19867         if(this.list && this.listWidth === undefined){
19868             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
19869             this.list.setWidth(lw);
19870             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19871         }
19872         
19873     
19874         
19875     },
19876     
19877     addItem: function(rec)
19878     {
19879         var valueField = this.combo.valueField;
19880         var displayField = this.combo.displayField;
19881         if (this.items.indexOfKey(rec[valueField]) > -1) {
19882             //console.log("GOT " + rec.data.id);
19883             return;
19884         }
19885         
19886         var x = new Roo.form.ComboBoxArray.Item({
19887             //id : rec[this.idField],
19888             data : rec,
19889             displayField : displayField ,
19890             tipField : displayField ,
19891             cb : this
19892         });
19893         // use the 
19894         this.items.add(rec[valueField],x);
19895         // add it before the element..
19896         this.updateHiddenEl();
19897         x.render(this.outerWrap, this.wrap.dom);
19898         // add the image handler..
19899     },
19900     
19901     updateHiddenEl : function()
19902     {
19903         this.validate();
19904         if (!this.hiddenEl) {
19905             return;
19906         }
19907         var ar = [];
19908         var idField = this.combo.valueField;
19909         
19910         this.items.each(function(f) {
19911             ar.push(f.data[idField]);
19912            
19913         });
19914         this.hiddenEl.dom.value = ar.join(',');
19915         this.validate();
19916     },
19917     
19918     reset : function()
19919     {
19920         this.items.clear();
19921         
19922         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
19923            el.remove();
19924         });
19925         
19926         this.el.dom.value = '';
19927         if (this.hiddenEl) {
19928             this.hiddenEl.dom.value = '';
19929         }
19930         
19931     },
19932     getValue: function()
19933     {
19934         return this.hiddenEl ? this.hiddenEl.dom.value : '';
19935     },
19936     setValue: function(v) // not a valid action - must use addItems..
19937     {
19938          
19939         this.reset();
19940         
19941         
19942         
19943         if (this.store.isLocal && (typeof(v) == 'string')) {
19944             // then we can use the store to find the values..
19945             // comma seperated at present.. this needs to allow JSON based encoding..
19946             this.hiddenEl.value  = v;
19947             var v_ar = [];
19948             Roo.each(v.split(','), function(k) {
19949                 Roo.log("CHECK " + this.valueField + ',' + k);
19950                 var li = this.store.query(this.valueField, k);
19951                 if (!li.length) {
19952                     return;
19953                 }
19954                 var add = {};
19955                 add[this.valueField] = k;
19956                 add[this.displayField] = li.item(0).data[this.displayField];
19957                 
19958                 this.addItem(add);
19959             }, this) 
19960              
19961         }
19962         if (typeof(v) == 'object' ) {
19963             // then let's assume it's an array of objects..
19964             Roo.each(v, function(l) {
19965                 this.addItem(l);
19966             }, this);
19967              
19968         }
19969         
19970         
19971     },
19972     setFromData: function(v)
19973     {
19974         // this recieves an object, if setValues is called.
19975         this.reset();
19976         this.el.dom.value = v[this.displayField];
19977         this.hiddenEl.dom.value = v[this.valueField];
19978         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
19979             return;
19980         }
19981         var kv = v[this.valueField];
19982         var dv = v[this.displayField];
19983         kv = typeof(kv) != 'string' ? '' : kv;
19984         dv = typeof(dv) != 'string' ? '' : dv;
19985         
19986         
19987         var keys = kv.split(',');
19988         var display = dv.split(',');
19989         for (var i = 0 ; i < keys.length; i++) {
19990             
19991             add = {};
19992             add[this.valueField] = keys[i];
19993             add[this.displayField] = display[i];
19994             this.addItem(add);
19995         }
19996       
19997         
19998     },
19999     
20000     /**
20001      * Validates the combox array value
20002      * @return {Boolean} True if the value is valid, else false
20003      */
20004     validate : function(){
20005         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
20006             this.clearInvalid();
20007             return true;
20008         }
20009         return false;
20010     },
20011     
20012     validateValue : function(value){
20013         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
20014         
20015     },
20016     
20017     /*@
20018      * overide
20019      * 
20020      */
20021     isDirty : function() {
20022         if(this.disabled) {
20023             return false;
20024         }
20025         
20026         try {
20027             var d = Roo.decode(String(this.originalValue));
20028         } catch (e) {
20029             return String(this.getValue()) !== String(this.originalValue);
20030         }
20031         
20032         var originalValue = [];
20033         
20034         for (var i = 0; i < d.length; i++){
20035             originalValue.push(d[i][this.valueField]);
20036         }
20037         
20038         return String(this.getValue()) !== String(originalValue.join(','));
20039         
20040     }
20041     
20042 });
20043
20044
20045
20046 /**
20047  * @class Roo.form.ComboBoxArray.Item
20048  * @extends Roo.BoxComponent
20049  * A selected item in the list
20050  *  Fred [x]  Brian [x]  [Pick another |v]
20051  * 
20052  * @constructor
20053  * Create a new item.
20054  * @param {Object} config Configuration options
20055  */
20056  
20057 Roo.form.ComboBoxArray.Item = function(config) {
20058     config.id = Roo.id();
20059     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
20060 }
20061
20062 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
20063     data : {},
20064     cb: false,
20065     displayField : false,
20066     tipField : false,
20067     
20068     
20069     defaultAutoCreate : {
20070         tag: 'div',
20071         cls: 'x-cbarray-item',
20072         cn : [ 
20073             { tag: 'div' },
20074             {
20075                 tag: 'img',
20076                 width:16,
20077                 height : 16,
20078                 src : Roo.BLANK_IMAGE_URL ,
20079                 align: 'center'
20080             }
20081         ]
20082         
20083     },
20084     
20085  
20086     onRender : function(ct, position)
20087     {
20088         Roo.form.Field.superclass.onRender.call(this, ct, position);
20089         
20090         if(!this.el){
20091             var cfg = this.getAutoCreate();
20092             this.el = ct.createChild(cfg, position);
20093         }
20094         
20095         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
20096         
20097         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
20098             this.cb.renderer(this.data) :
20099             String.format('{0}',this.data[this.displayField]);
20100         
20101             
20102         this.el.child('div').dom.setAttribute('qtip',
20103                         String.format('{0}',this.data[this.tipField])
20104         );
20105         
20106         this.el.child('img').on('click', this.remove, this);
20107         
20108     },
20109    
20110     remove : function()
20111     {
20112         if(this.cb.disabled){
20113             return;
20114         }
20115         
20116         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
20117             this.cb.items.remove(this);
20118             this.el.child('img').un('click', this.remove, this);
20119             this.el.remove();
20120             this.cb.updateHiddenEl();
20121
20122             this.cb.fireEvent('remove', this.cb, this);
20123         }
20124         
20125     }
20126 });/*
20127  * Based on:
20128  * Ext JS Library 1.1.1
20129  * Copyright(c) 2006-2007, Ext JS, LLC.
20130  *
20131  * Originally Released Under LGPL - original licence link has changed is not relivant.
20132  *
20133  * Fork - LGPL
20134  * <script type="text/javascript">
20135  */
20136 /**
20137  * @class Roo.form.Checkbox
20138  * @extends Roo.form.Field
20139  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
20140  * @constructor
20141  * Creates a new Checkbox
20142  * @param {Object} config Configuration options
20143  */
20144 Roo.form.Checkbox = function(config){
20145     Roo.form.Checkbox.superclass.constructor.call(this, config);
20146     this.addEvents({
20147         /**
20148          * @event check
20149          * Fires when the checkbox is checked or unchecked.
20150              * @param {Roo.form.Checkbox} this This checkbox
20151              * @param {Boolean} checked The new checked value
20152              */
20153         check : true
20154     });
20155 };
20156
20157 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
20158     /**
20159      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
20160      */
20161     focusClass : undefined,
20162     /**
20163      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
20164      */
20165     fieldClass: "x-form-field",
20166     /**
20167      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
20168      */
20169     checked: false,
20170     /**
20171      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20172      * {tag: "input", type: "checkbox", autocomplete: "off"})
20173      */
20174     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
20175     /**
20176      * @cfg {String} boxLabel The text that appears beside the checkbox
20177      */
20178     boxLabel : "",
20179     /**
20180      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
20181      */  
20182     inputValue : '1',
20183     /**
20184      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20185      */
20186      valueOff: '0', // value when not checked..
20187
20188     actionMode : 'viewEl', 
20189     //
20190     // private
20191     itemCls : 'x-menu-check-item x-form-item',
20192     groupClass : 'x-menu-group-item',
20193     inputType : 'hidden',
20194     
20195     
20196     inSetChecked: false, // check that we are not calling self...
20197     
20198     inputElement: false, // real input element?
20199     basedOn: false, // ????
20200     
20201     isFormField: true, // not sure where this is needed!!!!
20202
20203     onResize : function(){
20204         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
20205         if(!this.boxLabel){
20206             this.el.alignTo(this.wrap, 'c-c');
20207         }
20208     },
20209
20210     initEvents : function(){
20211         Roo.form.Checkbox.superclass.initEvents.call(this);
20212         this.el.on("click", this.onClick,  this);
20213         this.el.on("change", this.onClick,  this);
20214     },
20215
20216
20217     getResizeEl : function(){
20218         return this.wrap;
20219     },
20220
20221     getPositionEl : function(){
20222         return this.wrap;
20223     },
20224
20225     // private
20226     onRender : function(ct, position){
20227         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20228         /*
20229         if(this.inputValue !== undefined){
20230             this.el.dom.value = this.inputValue;
20231         }
20232         */
20233         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20234         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20235         var viewEl = this.wrap.createChild({ 
20236             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20237         this.viewEl = viewEl;   
20238         this.wrap.on('click', this.onClick,  this); 
20239         
20240         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20241         this.el.on('propertychange', this.setFromHidden,  this);  //ie
20242         
20243         
20244         
20245         if(this.boxLabel){
20246             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20247         //    viewEl.on('click', this.onClick,  this); 
20248         }
20249         //if(this.checked){
20250             this.setChecked(this.checked);
20251         //}else{
20252             //this.checked = this.el.dom;
20253         //}
20254
20255     },
20256
20257     // private
20258     initValue : Roo.emptyFn,
20259
20260     /**
20261      * Returns the checked state of the checkbox.
20262      * @return {Boolean} True if checked, else false
20263      */
20264     getValue : function(){
20265         if(this.el){
20266             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
20267         }
20268         return this.valueOff;
20269         
20270     },
20271
20272         // private
20273     onClick : function(){ 
20274         if (this.disabled) {
20275             return;
20276         }
20277         this.setChecked(!this.checked);
20278
20279         //if(this.el.dom.checked != this.checked){
20280         //    this.setValue(this.el.dom.checked);
20281        // }
20282     },
20283
20284     /**
20285      * Sets the checked state of the checkbox.
20286      * On is always based on a string comparison between inputValue and the param.
20287      * @param {Boolean/String} value - the value to set 
20288      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
20289      */
20290     setValue : function(v,suppressEvent){
20291         
20292         
20293         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
20294         //if(this.el && this.el.dom){
20295         //    this.el.dom.checked = this.checked;
20296         //    this.el.dom.defaultChecked = this.checked;
20297         //}
20298         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
20299         //this.fireEvent("check", this, this.checked);
20300     },
20301     // private..
20302     setChecked : function(state,suppressEvent)
20303     {
20304         if (this.inSetChecked) {
20305             this.checked = state;
20306             return;
20307         }
20308         
20309     
20310         if(this.wrap){
20311             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
20312         }
20313         this.checked = state;
20314         if(suppressEvent !== true){
20315             this.fireEvent('check', this, state);
20316         }
20317         this.inSetChecked = true;
20318         this.el.dom.value = state ? this.inputValue : this.valueOff;
20319         this.inSetChecked = false;
20320         
20321     },
20322     // handle setting of hidden value by some other method!!?!?
20323     setFromHidden: function()
20324     {
20325         if(!this.el){
20326             return;
20327         }
20328         //console.log("SET FROM HIDDEN");
20329         //alert('setFrom hidden');
20330         this.setValue(this.el.dom.value);
20331     },
20332     
20333     onDestroy : function()
20334     {
20335         if(this.viewEl){
20336             Roo.get(this.viewEl).remove();
20337         }
20338          
20339         Roo.form.Checkbox.superclass.onDestroy.call(this);
20340     },
20341     
20342     setBoxLabel : function(str)
20343     {
20344         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
20345     }
20346
20347 });/*
20348  * Based on:
20349  * Ext JS Library 1.1.1
20350  * Copyright(c) 2006-2007, Ext JS, LLC.
20351  *
20352  * Originally Released Under LGPL - original licence link has changed is not relivant.
20353  *
20354  * Fork - LGPL
20355  * <script type="text/javascript">
20356  */
20357  
20358 /**
20359  * @class Roo.form.Radio
20360  * @extends Roo.form.Checkbox
20361  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
20362  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
20363  * @constructor
20364  * Creates a new Radio
20365  * @param {Object} config Configuration options
20366  */
20367 Roo.form.Radio = function(){
20368     Roo.form.Radio.superclass.constructor.apply(this, arguments);
20369 };
20370 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
20371     inputType: 'radio',
20372
20373     /**
20374      * If this radio is part of a group, it will return the selected value
20375      * @return {String}
20376      */
20377     getGroupValue : function(){
20378         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
20379     },
20380     
20381     
20382     onRender : function(ct, position){
20383         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20384         
20385         if(this.inputValue !== undefined){
20386             this.el.dom.value = this.inputValue;
20387         }
20388          
20389         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20390         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20391         //var viewEl = this.wrap.createChild({ 
20392         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20393         //this.viewEl = viewEl;   
20394         //this.wrap.on('click', this.onClick,  this); 
20395         
20396         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20397         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
20398         
20399         
20400         
20401         if(this.boxLabel){
20402             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20403         //    viewEl.on('click', this.onClick,  this); 
20404         }
20405          if(this.checked){
20406             this.el.dom.checked =   'checked' ;
20407         }
20408          
20409     } 
20410     
20411     
20412 });//<script type="text/javascript">
20413
20414 /*
20415  * Based  Ext JS Library 1.1.1
20416  * Copyright(c) 2006-2007, Ext JS, LLC.
20417  * LGPL
20418  *
20419  */
20420  
20421 /**
20422  * @class Roo.HtmlEditorCore
20423  * @extends Roo.Component
20424  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20425  *
20426  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20427  */
20428
20429 Roo.HtmlEditorCore = function(config){
20430     
20431     
20432     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20433     
20434     
20435     this.addEvents({
20436         /**
20437          * @event initialize
20438          * Fires when the editor is fully initialized (including the iframe)
20439          * @param {Roo.HtmlEditorCore} this
20440          */
20441         initialize: true,
20442         /**
20443          * @event activate
20444          * Fires when the editor is first receives the focus. Any insertion must wait
20445          * until after this event.
20446          * @param {Roo.HtmlEditorCore} this
20447          */
20448         activate: true,
20449          /**
20450          * @event beforesync
20451          * Fires before the textarea is updated with content from the editor iframe. Return false
20452          * to cancel the sync.
20453          * @param {Roo.HtmlEditorCore} this
20454          * @param {String} html
20455          */
20456         beforesync: true,
20457          /**
20458          * @event beforepush
20459          * Fires before the iframe editor is updated with content from the textarea. Return false
20460          * to cancel the push.
20461          * @param {Roo.HtmlEditorCore} this
20462          * @param {String} html
20463          */
20464         beforepush: true,
20465          /**
20466          * @event sync
20467          * Fires when the textarea is updated with content from the editor iframe.
20468          * @param {Roo.HtmlEditorCore} this
20469          * @param {String} html
20470          */
20471         sync: true,
20472          /**
20473          * @event push
20474          * Fires when the iframe editor is updated with content from the textarea.
20475          * @param {Roo.HtmlEditorCore} this
20476          * @param {String} html
20477          */
20478         push: true,
20479         
20480         /**
20481          * @event editorevent
20482          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20483          * @param {Roo.HtmlEditorCore} this
20484          */
20485         editorevent: true
20486         
20487     });
20488     
20489     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20490     
20491     // defaults : white / black...
20492     this.applyBlacklists();
20493     
20494     
20495     
20496 };
20497
20498
20499 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
20500
20501
20502      /**
20503      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
20504      */
20505     
20506     owner : false,
20507     
20508      /**
20509      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20510      *                        Roo.resizable.
20511      */
20512     resizable : false,
20513      /**
20514      * @cfg {Number} height (in pixels)
20515      */   
20516     height: 300,
20517    /**
20518      * @cfg {Number} width (in pixels)
20519      */   
20520     width: 500,
20521     
20522     /**
20523      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20524      * 
20525      */
20526     stylesheets: false,
20527     
20528     // id of frame..
20529     frameId: false,
20530     
20531     // private properties
20532     validationEvent : false,
20533     deferHeight: true,
20534     initialized : false,
20535     activated : false,
20536     sourceEditMode : false,
20537     onFocus : Roo.emptyFn,
20538     iframePad:3,
20539     hideMode:'offsets',
20540     
20541     clearUp: true,
20542     
20543     // blacklist + whitelisted elements..
20544     black: false,
20545     white: false,
20546      
20547     bodyCls : '',
20548
20549     /**
20550      * Protected method that will not generally be called directly. It
20551      * is called when the editor initializes the iframe with HTML contents. Override this method if you
20552      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20553      */
20554     getDocMarkup : function(){
20555         // body styles..
20556         var st = '';
20557         
20558         // inherit styels from page...?? 
20559         if (this.stylesheets === false) {
20560             
20561             Roo.get(document.head).select('style').each(function(node) {
20562                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20563             });
20564             
20565             Roo.get(document.head).select('link').each(function(node) { 
20566                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20567             });
20568             
20569         } else if (!this.stylesheets.length) {
20570                 // simple..
20571                 st = '<style type="text/css">' +
20572                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20573                    '</style>';
20574         } else { 
20575             st = '<style type="text/css">' +
20576                     this.stylesheets +
20577                 '</style>';
20578         }
20579         
20580         st +=  '<style type="text/css">' +
20581             'IMG { cursor: pointer } ' +
20582         '</style>';
20583
20584         var cls = 'roo-htmleditor-body';
20585         
20586         if(this.bodyCls.length){
20587             cls += ' ' + this.bodyCls;
20588         }
20589         
20590         return '<html><head>' + st  +
20591             //<style type="text/css">' +
20592             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20593             //'</style>' +
20594             ' </head><body class="' +  cls + '"></body></html>';
20595     },
20596
20597     // private
20598     onRender : function(ct, position)
20599     {
20600         var _t = this;
20601         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20602         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20603         
20604         
20605         this.el.dom.style.border = '0 none';
20606         this.el.dom.setAttribute('tabIndex', -1);
20607         this.el.addClass('x-hidden hide');
20608         
20609         
20610         
20611         if(Roo.isIE){ // fix IE 1px bogus margin
20612             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20613         }
20614        
20615         
20616         this.frameId = Roo.id();
20617         
20618          
20619         
20620         var iframe = this.owner.wrap.createChild({
20621             tag: 'iframe',
20622             cls: 'form-control', // bootstrap..
20623             id: this.frameId,
20624             name: this.frameId,
20625             frameBorder : 'no',
20626             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
20627         }, this.el
20628         );
20629         
20630         
20631         this.iframe = iframe.dom;
20632
20633          this.assignDocWin();
20634         
20635         this.doc.designMode = 'on';
20636        
20637         this.doc.open();
20638         this.doc.write(this.getDocMarkup());
20639         this.doc.close();
20640
20641         
20642         var task = { // must defer to wait for browser to be ready
20643             run : function(){
20644                 //console.log("run task?" + this.doc.readyState);
20645                 this.assignDocWin();
20646                 if(this.doc.body || this.doc.readyState == 'complete'){
20647                     try {
20648                         this.doc.designMode="on";
20649                     } catch (e) {
20650                         return;
20651                     }
20652                     Roo.TaskMgr.stop(task);
20653                     this.initEditor.defer(10, this);
20654                 }
20655             },
20656             interval : 10,
20657             duration: 10000,
20658             scope: this
20659         };
20660         Roo.TaskMgr.start(task);
20661
20662     },
20663
20664     // private
20665     onResize : function(w, h)
20666     {
20667          Roo.log('resize: ' +w + ',' + h );
20668         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20669         if(!this.iframe){
20670             return;
20671         }
20672         if(typeof w == 'number'){
20673             
20674             this.iframe.style.width = w + 'px';
20675         }
20676         if(typeof h == 'number'){
20677             
20678             this.iframe.style.height = h + 'px';
20679             if(this.doc){
20680                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20681             }
20682         }
20683         
20684     },
20685
20686     /**
20687      * Toggles the editor between standard and source edit mode.
20688      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20689      */
20690     toggleSourceEdit : function(sourceEditMode){
20691         
20692         this.sourceEditMode = sourceEditMode === true;
20693         
20694         if(this.sourceEditMode){
20695  
20696             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
20697             
20698         }else{
20699             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20700             //this.iframe.className = '';
20701             this.deferFocus();
20702         }
20703         //this.setSize(this.owner.wrap.getSize());
20704         //this.fireEvent('editmodechange', this, this.sourceEditMode);
20705     },
20706
20707     
20708   
20709
20710     /**
20711      * Protected method that will not generally be called directly. If you need/want
20712      * custom HTML cleanup, this is the method you should override.
20713      * @param {String} html The HTML to be cleaned
20714      * return {String} The cleaned HTML
20715      */
20716     cleanHtml : function(html){
20717         html = String(html);
20718         if(html.length > 5){
20719             if(Roo.isSafari){ // strip safari nonsense
20720                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20721             }
20722         }
20723         if(html == '&nbsp;'){
20724             html = '';
20725         }
20726         return html;
20727     },
20728
20729     /**
20730      * HTML Editor -> Textarea
20731      * Protected method that will not generally be called directly. Syncs the contents
20732      * of the editor iframe with the textarea.
20733      */
20734     syncValue : function(){
20735         if(this.initialized){
20736             var bd = (this.doc.body || this.doc.documentElement);
20737             //this.cleanUpPaste(); -- this is done else where and causes havoc..
20738             var html = bd.innerHTML;
20739             if(Roo.isSafari){
20740                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20741                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20742                 if(m && m[1]){
20743                     html = '<div style="'+m[0]+'">' + html + '</div>';
20744                 }
20745             }
20746             html = this.cleanHtml(html);
20747             // fix up the special chars.. normaly like back quotes in word...
20748             // however we do not want to do this with chinese..
20749             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
20750                 var cc = b.charCodeAt();
20751                 if (
20752                     (cc >= 0x4E00 && cc < 0xA000 ) ||
20753                     (cc >= 0x3400 && cc < 0x4E00 ) ||
20754                     (cc >= 0xf900 && cc < 0xfb00 )
20755                 ) {
20756                         return b;
20757                 }
20758                 return "&#"+cc+";" 
20759             });
20760             if(this.owner.fireEvent('beforesync', this, html) !== false){
20761                 this.el.dom.value = html;
20762                 this.owner.fireEvent('sync', this, html);
20763             }
20764         }
20765     },
20766
20767     /**
20768      * Protected method that will not generally be called directly. Pushes the value of the textarea
20769      * into the iframe editor.
20770      */
20771     pushValue : function(){
20772         if(this.initialized){
20773             var v = this.el.dom.value.trim();
20774             
20775 //            if(v.length < 1){
20776 //                v = '&#160;';
20777 //            }
20778             
20779             if(this.owner.fireEvent('beforepush', this, v) !== false){
20780                 var d = (this.doc.body || this.doc.documentElement);
20781                 d.innerHTML = v;
20782                 this.cleanUpPaste();
20783                 this.el.dom.value = d.innerHTML;
20784                 this.owner.fireEvent('push', this, v);
20785             }
20786         }
20787     },
20788
20789     // private
20790     deferFocus : function(){
20791         this.focus.defer(10, this);
20792     },
20793
20794     // doc'ed in Field
20795     focus : function(){
20796         if(this.win && !this.sourceEditMode){
20797             this.win.focus();
20798         }else{
20799             this.el.focus();
20800         }
20801     },
20802     
20803     assignDocWin: function()
20804     {
20805         var iframe = this.iframe;
20806         
20807          if(Roo.isIE){
20808             this.doc = iframe.contentWindow.document;
20809             this.win = iframe.contentWindow;
20810         } else {
20811 //            if (!Roo.get(this.frameId)) {
20812 //                return;
20813 //            }
20814 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20815 //            this.win = Roo.get(this.frameId).dom.contentWindow;
20816             
20817             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20818                 return;
20819             }
20820             
20821             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20822             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20823         }
20824     },
20825     
20826     // private
20827     initEditor : function(){
20828         //console.log("INIT EDITOR");
20829         this.assignDocWin();
20830         
20831         
20832         
20833         this.doc.designMode="on";
20834         this.doc.open();
20835         this.doc.write(this.getDocMarkup());
20836         this.doc.close();
20837         
20838         var dbody = (this.doc.body || this.doc.documentElement);
20839         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20840         // this copies styles from the containing element into thsi one..
20841         // not sure why we need all of this..
20842         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20843         
20844         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20845         //ss['background-attachment'] = 'fixed'; // w3c
20846         dbody.bgProperties = 'fixed'; // ie
20847         //Roo.DomHelper.applyStyles(dbody, ss);
20848         Roo.EventManager.on(this.doc, {
20849             //'mousedown': this.onEditorEvent,
20850             'mouseup': this.onEditorEvent,
20851             'dblclick': this.onEditorEvent,
20852             'click': this.onEditorEvent,
20853             'keyup': this.onEditorEvent,
20854             buffer:100,
20855             scope: this
20856         });
20857         if(Roo.isGecko){
20858             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20859         }
20860         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20861             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20862         }
20863         this.initialized = true;
20864
20865         this.owner.fireEvent('initialize', this);
20866         this.pushValue();
20867     },
20868
20869     // private
20870     onDestroy : function(){
20871         
20872         
20873         
20874         if(this.rendered){
20875             
20876             //for (var i =0; i < this.toolbars.length;i++) {
20877             //    // fixme - ask toolbars for heights?
20878             //    this.toolbars[i].onDestroy();
20879            // }
20880             
20881             //this.wrap.dom.innerHTML = '';
20882             //this.wrap.remove();
20883         }
20884     },
20885
20886     // private
20887     onFirstFocus : function(){
20888         
20889         this.assignDocWin();
20890         
20891         
20892         this.activated = true;
20893          
20894     
20895         if(Roo.isGecko){ // prevent silly gecko errors
20896             this.win.focus();
20897             var s = this.win.getSelection();
20898             if(!s.focusNode || s.focusNode.nodeType != 3){
20899                 var r = s.getRangeAt(0);
20900                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
20901                 r.collapse(true);
20902                 this.deferFocus();
20903             }
20904             try{
20905                 this.execCmd('useCSS', true);
20906                 this.execCmd('styleWithCSS', false);
20907             }catch(e){}
20908         }
20909         this.owner.fireEvent('activate', this);
20910     },
20911
20912     // private
20913     adjustFont: function(btn){
20914         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
20915         //if(Roo.isSafari){ // safari
20916         //    adjust *= 2;
20917        // }
20918         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
20919         if(Roo.isSafari){ // safari
20920             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
20921             v =  (v < 10) ? 10 : v;
20922             v =  (v > 48) ? 48 : v;
20923             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
20924             
20925         }
20926         
20927         
20928         v = Math.max(1, v+adjust);
20929         
20930         this.execCmd('FontSize', v  );
20931     },
20932
20933     onEditorEvent : function(e)
20934     {
20935         this.owner.fireEvent('editorevent', this, e);
20936       //  this.updateToolbar();
20937         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
20938     },
20939
20940     insertTag : function(tg)
20941     {
20942         // could be a bit smarter... -> wrap the current selected tRoo..
20943         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
20944             
20945             range = this.createRange(this.getSelection());
20946             var wrappingNode = this.doc.createElement(tg.toLowerCase());
20947             wrappingNode.appendChild(range.extractContents());
20948             range.insertNode(wrappingNode);
20949
20950             return;
20951             
20952             
20953             
20954         }
20955         this.execCmd("formatblock",   tg);
20956         
20957     },
20958     
20959     insertText : function(txt)
20960     {
20961         
20962         
20963         var range = this.createRange();
20964         range.deleteContents();
20965                //alert(Sender.getAttribute('label'));
20966                
20967         range.insertNode(this.doc.createTextNode(txt));
20968     } ,
20969     
20970      
20971
20972     /**
20973      * Executes a Midas editor command on the editor document and performs necessary focus and
20974      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
20975      * @param {String} cmd The Midas command
20976      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20977      */
20978     relayCmd : function(cmd, value){
20979         this.win.focus();
20980         this.execCmd(cmd, value);
20981         this.owner.fireEvent('editorevent', this);
20982         //this.updateToolbar();
20983         this.owner.deferFocus();
20984     },
20985
20986     /**
20987      * Executes a Midas editor command directly on the editor document.
20988      * For visual commands, you should use {@link #relayCmd} instead.
20989      * <b>This should only be called after the editor is initialized.</b>
20990      * @param {String} cmd The Midas command
20991      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20992      */
20993     execCmd : function(cmd, value){
20994         this.doc.execCommand(cmd, false, value === undefined ? null : value);
20995         this.syncValue();
20996     },
20997  
20998  
20999    
21000     /**
21001      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21002      * to insert tRoo.
21003      * @param {String} text | dom node.. 
21004      */
21005     insertAtCursor : function(text)
21006     {
21007         
21008         if(!this.activated){
21009             return;
21010         }
21011         /*
21012         if(Roo.isIE){
21013             this.win.focus();
21014             var r = this.doc.selection.createRange();
21015             if(r){
21016                 r.collapse(true);
21017                 r.pasteHTML(text);
21018                 this.syncValue();
21019                 this.deferFocus();
21020             
21021             }
21022             return;
21023         }
21024         */
21025         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21026             this.win.focus();
21027             
21028             
21029             // from jquery ui (MIT licenced)
21030             var range, node;
21031             var win = this.win;
21032             
21033             if (win.getSelection && win.getSelection().getRangeAt) {
21034                 range = win.getSelection().getRangeAt(0);
21035                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21036                 range.insertNode(node);
21037             } else if (win.document.selection && win.document.selection.createRange) {
21038                 // no firefox support
21039                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21040                 win.document.selection.createRange().pasteHTML(txt);
21041             } else {
21042                 // no firefox support
21043                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21044                 this.execCmd('InsertHTML', txt);
21045             } 
21046             
21047             this.syncValue();
21048             
21049             this.deferFocus();
21050         }
21051     },
21052  // private
21053     mozKeyPress : function(e){
21054         if(e.ctrlKey){
21055             var c = e.getCharCode(), cmd;
21056           
21057             if(c > 0){
21058                 c = String.fromCharCode(c).toLowerCase();
21059                 switch(c){
21060                     case 'b':
21061                         cmd = 'bold';
21062                         break;
21063                     case 'i':
21064                         cmd = 'italic';
21065                         break;
21066                     
21067                     case 'u':
21068                         cmd = 'underline';
21069                         break;
21070                     
21071                     case 'v':
21072                         this.cleanUpPaste.defer(100, this);
21073                         return;
21074                         
21075                 }
21076                 if(cmd){
21077                     this.win.focus();
21078                     this.execCmd(cmd);
21079                     this.deferFocus();
21080                     e.preventDefault();
21081                 }
21082                 
21083             }
21084         }
21085     },
21086
21087     // private
21088     fixKeys : function(){ // load time branching for fastest keydown performance
21089         if(Roo.isIE){
21090             return function(e){
21091                 var k = e.getKey(), r;
21092                 if(k == e.TAB){
21093                     e.stopEvent();
21094                     r = this.doc.selection.createRange();
21095                     if(r){
21096                         r.collapse(true);
21097                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21098                         this.deferFocus();
21099                     }
21100                     return;
21101                 }
21102                 
21103                 if(k == e.ENTER){
21104                     r = this.doc.selection.createRange();
21105                     if(r){
21106                         var target = r.parentElement();
21107                         if(!target || target.tagName.toLowerCase() != 'li'){
21108                             e.stopEvent();
21109                             r.pasteHTML('<br />');
21110                             r.collapse(false);
21111                             r.select();
21112                         }
21113                     }
21114                 }
21115                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21116                     this.cleanUpPaste.defer(100, this);
21117                     return;
21118                 }
21119                 
21120                 
21121             };
21122         }else if(Roo.isOpera){
21123             return function(e){
21124                 var k = e.getKey();
21125                 if(k == e.TAB){
21126                     e.stopEvent();
21127                     this.win.focus();
21128                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21129                     this.deferFocus();
21130                 }
21131                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21132                     this.cleanUpPaste.defer(100, this);
21133                     return;
21134                 }
21135                 
21136             };
21137         }else if(Roo.isSafari){
21138             return function(e){
21139                 var k = e.getKey();
21140                 
21141                 if(k == e.TAB){
21142                     e.stopEvent();
21143                     this.execCmd('InsertText','\t');
21144                     this.deferFocus();
21145                     return;
21146                 }
21147                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21148                     this.cleanUpPaste.defer(100, this);
21149                     return;
21150                 }
21151                 
21152              };
21153         }
21154     }(),
21155     
21156     getAllAncestors: function()
21157     {
21158         var p = this.getSelectedNode();
21159         var a = [];
21160         if (!p) {
21161             a.push(p); // push blank onto stack..
21162             p = this.getParentElement();
21163         }
21164         
21165         
21166         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21167             a.push(p);
21168             p = p.parentNode;
21169         }
21170         a.push(this.doc.body);
21171         return a;
21172     },
21173     lastSel : false,
21174     lastSelNode : false,
21175     
21176     
21177     getSelection : function() 
21178     {
21179         this.assignDocWin();
21180         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21181     },
21182     
21183     getSelectedNode: function() 
21184     {
21185         // this may only work on Gecko!!!
21186         
21187         // should we cache this!!!!
21188         
21189         
21190         
21191          
21192         var range = this.createRange(this.getSelection()).cloneRange();
21193         
21194         if (Roo.isIE) {
21195             var parent = range.parentElement();
21196             while (true) {
21197                 var testRange = range.duplicate();
21198                 testRange.moveToElementText(parent);
21199                 if (testRange.inRange(range)) {
21200                     break;
21201                 }
21202                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21203                     break;
21204                 }
21205                 parent = parent.parentElement;
21206             }
21207             return parent;
21208         }
21209         
21210         // is ancestor a text element.
21211         var ac =  range.commonAncestorContainer;
21212         if (ac.nodeType == 3) {
21213             ac = ac.parentNode;
21214         }
21215         
21216         var ar = ac.childNodes;
21217          
21218         var nodes = [];
21219         var other_nodes = [];
21220         var has_other_nodes = false;
21221         for (var i=0;i<ar.length;i++) {
21222             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
21223                 continue;
21224             }
21225             // fullly contained node.
21226             
21227             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21228                 nodes.push(ar[i]);
21229                 continue;
21230             }
21231             
21232             // probably selected..
21233             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21234                 other_nodes.push(ar[i]);
21235                 continue;
21236             }
21237             // outer..
21238             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
21239                 continue;
21240             }
21241             
21242             
21243             has_other_nodes = true;
21244         }
21245         if (!nodes.length && other_nodes.length) {
21246             nodes= other_nodes;
21247         }
21248         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21249             return false;
21250         }
21251         
21252         return nodes[0];
21253     },
21254     createRange: function(sel)
21255     {
21256         // this has strange effects when using with 
21257         // top toolbar - not sure if it's a great idea.
21258         //this.editor.contentWindow.focus();
21259         if (typeof sel != "undefined") {
21260             try {
21261                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21262             } catch(e) {
21263                 return this.doc.createRange();
21264             }
21265         } else {
21266             return this.doc.createRange();
21267         }
21268     },
21269     getParentElement: function()
21270     {
21271         
21272         this.assignDocWin();
21273         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21274         
21275         var range = this.createRange(sel);
21276          
21277         try {
21278             var p = range.commonAncestorContainer;
21279             while (p.nodeType == 3) { // text node
21280                 p = p.parentNode;
21281             }
21282             return p;
21283         } catch (e) {
21284             return null;
21285         }
21286     
21287     },
21288     /***
21289      *
21290      * Range intersection.. the hard stuff...
21291      *  '-1' = before
21292      *  '0' = hits..
21293      *  '1' = after.
21294      *         [ -- selected range --- ]
21295      *   [fail]                        [fail]
21296      *
21297      *    basically..
21298      *      if end is before start or  hits it. fail.
21299      *      if start is after end or hits it fail.
21300      *
21301      *   if either hits (but other is outside. - then it's not 
21302      *   
21303      *    
21304      **/
21305     
21306     
21307     // @see http://www.thismuchiknow.co.uk/?p=64.
21308     rangeIntersectsNode : function(range, node)
21309     {
21310         var nodeRange = node.ownerDocument.createRange();
21311         try {
21312             nodeRange.selectNode(node);
21313         } catch (e) {
21314             nodeRange.selectNodeContents(node);
21315         }
21316     
21317         var rangeStartRange = range.cloneRange();
21318         rangeStartRange.collapse(true);
21319     
21320         var rangeEndRange = range.cloneRange();
21321         rangeEndRange.collapse(false);
21322     
21323         var nodeStartRange = nodeRange.cloneRange();
21324         nodeStartRange.collapse(true);
21325     
21326         var nodeEndRange = nodeRange.cloneRange();
21327         nodeEndRange.collapse(false);
21328     
21329         return rangeStartRange.compareBoundaryPoints(
21330                  Range.START_TO_START, nodeEndRange) == -1 &&
21331                rangeEndRange.compareBoundaryPoints(
21332                  Range.START_TO_START, nodeStartRange) == 1;
21333         
21334          
21335     },
21336     rangeCompareNode : function(range, node)
21337     {
21338         var nodeRange = node.ownerDocument.createRange();
21339         try {
21340             nodeRange.selectNode(node);
21341         } catch (e) {
21342             nodeRange.selectNodeContents(node);
21343         }
21344         
21345         
21346         range.collapse(true);
21347     
21348         nodeRange.collapse(true);
21349      
21350         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21351         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
21352          
21353         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21354         
21355         var nodeIsBefore   =  ss == 1;
21356         var nodeIsAfter    = ee == -1;
21357         
21358         if (nodeIsBefore && nodeIsAfter) {
21359             return 0; // outer
21360         }
21361         if (!nodeIsBefore && nodeIsAfter) {
21362             return 1; //right trailed.
21363         }
21364         
21365         if (nodeIsBefore && !nodeIsAfter) {
21366             return 2;  // left trailed.
21367         }
21368         // fully contined.
21369         return 3;
21370     },
21371
21372     // private? - in a new class?
21373     cleanUpPaste :  function()
21374     {
21375         // cleans up the whole document..
21376         Roo.log('cleanuppaste');
21377         
21378         this.cleanUpChildren(this.doc.body);
21379         var clean = this.cleanWordChars(this.doc.body.innerHTML);
21380         if (clean != this.doc.body.innerHTML) {
21381             this.doc.body.innerHTML = clean;
21382         }
21383         
21384     },
21385     
21386     cleanWordChars : function(input) {// change the chars to hex code
21387         var he = Roo.HtmlEditorCore;
21388         
21389         var output = input;
21390         Roo.each(he.swapCodes, function(sw) { 
21391             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21392             
21393             output = output.replace(swapper, sw[1]);
21394         });
21395         
21396         return output;
21397     },
21398     
21399     
21400     cleanUpChildren : function (n)
21401     {
21402         if (!n.childNodes.length) {
21403             return;
21404         }
21405         for (var i = n.childNodes.length-1; i > -1 ; i--) {
21406            this.cleanUpChild(n.childNodes[i]);
21407         }
21408     },
21409     
21410     
21411         
21412     
21413     cleanUpChild : function (node)
21414     {
21415         var ed = this;
21416         //console.log(node);
21417         if (node.nodeName == "#text") {
21418             // clean up silly Windows -- stuff?
21419             return; 
21420         }
21421         if (node.nodeName == "#comment") {
21422             node.parentNode.removeChild(node);
21423             // clean up silly Windows -- stuff?
21424             return; 
21425         }
21426         var lcname = node.tagName.toLowerCase();
21427         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21428         // whitelist of tags..
21429         
21430         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21431             // remove node.
21432             node.parentNode.removeChild(node);
21433             return;
21434             
21435         }
21436         
21437         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21438         
21439         // remove <a name=....> as rendering on yahoo mailer is borked with this.
21440         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21441         
21442         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21443         //    remove_keep_children = true;
21444         //}
21445         
21446         if (remove_keep_children) {
21447             this.cleanUpChildren(node);
21448             // inserts everything just before this node...
21449             while (node.childNodes.length) {
21450                 var cn = node.childNodes[0];
21451                 node.removeChild(cn);
21452                 node.parentNode.insertBefore(cn, node);
21453             }
21454             node.parentNode.removeChild(node);
21455             return;
21456         }
21457         
21458         if (!node.attributes || !node.attributes.length) {
21459             this.cleanUpChildren(node);
21460             return;
21461         }
21462         
21463         function cleanAttr(n,v)
21464         {
21465             
21466             if (v.match(/^\./) || v.match(/^\//)) {
21467                 return;
21468             }
21469             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
21470                 return;
21471             }
21472             if (v.match(/^#/)) {
21473                 return;
21474             }
21475 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21476             node.removeAttribute(n);
21477             
21478         }
21479         
21480         var cwhite = this.cwhite;
21481         var cblack = this.cblack;
21482             
21483         function cleanStyle(n,v)
21484         {
21485             if (v.match(/expression/)) { //XSS?? should we even bother..
21486                 node.removeAttribute(n);
21487                 return;
21488             }
21489             
21490             var parts = v.split(/;/);
21491             var clean = [];
21492             
21493             Roo.each(parts, function(p) {
21494                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21495                 if (!p.length) {
21496                     return true;
21497                 }
21498                 var l = p.split(':').shift().replace(/\s+/g,'');
21499                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21500                 
21501                 if ( cwhite.length && cblack.indexOf(l) > -1) {
21502 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21503                     //node.removeAttribute(n);
21504                     return true;
21505                 }
21506                 //Roo.log()
21507                 // only allow 'c whitelisted system attributes'
21508                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
21509 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21510                     //node.removeAttribute(n);
21511                     return true;
21512                 }
21513                 
21514                 
21515                  
21516                 
21517                 clean.push(p);
21518                 return true;
21519             });
21520             if (clean.length) { 
21521                 node.setAttribute(n, clean.join(';'));
21522             } else {
21523                 node.removeAttribute(n);
21524             }
21525             
21526         }
21527         
21528         
21529         for (var i = node.attributes.length-1; i > -1 ; i--) {
21530             var a = node.attributes[i];
21531             //console.log(a);
21532             
21533             if (a.name.toLowerCase().substr(0,2)=='on')  {
21534                 node.removeAttribute(a.name);
21535                 continue;
21536             }
21537             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21538                 node.removeAttribute(a.name);
21539                 continue;
21540             }
21541             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21542                 cleanAttr(a.name,a.value); // fixme..
21543                 continue;
21544             }
21545             if (a.name == 'style') {
21546                 cleanStyle(a.name,a.value);
21547                 continue;
21548             }
21549             /// clean up MS crap..
21550             // tecnically this should be a list of valid class'es..
21551             
21552             
21553             if (a.name == 'class') {
21554                 if (a.value.match(/^Mso/)) {
21555                     node.className = '';
21556                 }
21557                 
21558                 if (a.value.match(/^body$/)) {
21559                     node.className = '';
21560                 }
21561                 continue;
21562             }
21563             
21564             // style cleanup!?
21565             // class cleanup?
21566             
21567         }
21568         
21569         
21570         this.cleanUpChildren(node);
21571         
21572         
21573     },
21574     
21575     /**
21576      * Clean up MS wordisms...
21577      */
21578     cleanWord : function(node)
21579     {
21580         
21581         
21582         if (!node) {
21583             this.cleanWord(this.doc.body);
21584             return;
21585         }
21586         if (node.nodeName == "#text") {
21587             // clean up silly Windows -- stuff?
21588             return; 
21589         }
21590         if (node.nodeName == "#comment") {
21591             node.parentNode.removeChild(node);
21592             // clean up silly Windows -- stuff?
21593             return; 
21594         }
21595         
21596         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21597             node.parentNode.removeChild(node);
21598             return;
21599         }
21600         
21601         // remove - but keep children..
21602         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
21603             while (node.childNodes.length) {
21604                 var cn = node.childNodes[0];
21605                 node.removeChild(cn);
21606                 node.parentNode.insertBefore(cn, node);
21607             }
21608             node.parentNode.removeChild(node);
21609             this.iterateChildren(node, this.cleanWord);
21610             return;
21611         }
21612         // clean styles
21613         if (node.className.length) {
21614             
21615             var cn = node.className.split(/\W+/);
21616             var cna = [];
21617             Roo.each(cn, function(cls) {
21618                 if (cls.match(/Mso[a-zA-Z]+/)) {
21619                     return;
21620                 }
21621                 cna.push(cls);
21622             });
21623             node.className = cna.length ? cna.join(' ') : '';
21624             if (!cna.length) {
21625                 node.removeAttribute("class");
21626             }
21627         }
21628         
21629         if (node.hasAttribute("lang")) {
21630             node.removeAttribute("lang");
21631         }
21632         
21633         if (node.hasAttribute("style")) {
21634             
21635             var styles = node.getAttribute("style").split(";");
21636             var nstyle = [];
21637             Roo.each(styles, function(s) {
21638                 if (!s.match(/:/)) {
21639                     return;
21640                 }
21641                 var kv = s.split(":");
21642                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21643                     return;
21644                 }
21645                 // what ever is left... we allow.
21646                 nstyle.push(s);
21647             });
21648             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21649             if (!nstyle.length) {
21650                 node.removeAttribute('style');
21651             }
21652         }
21653         this.iterateChildren(node, this.cleanWord);
21654         
21655         
21656         
21657     },
21658     /**
21659      * iterateChildren of a Node, calling fn each time, using this as the scole..
21660      * @param {DomNode} node node to iterate children of.
21661      * @param {Function} fn method of this class to call on each item.
21662      */
21663     iterateChildren : function(node, fn)
21664     {
21665         if (!node.childNodes.length) {
21666                 return;
21667         }
21668         for (var i = node.childNodes.length-1; i > -1 ; i--) {
21669            fn.call(this, node.childNodes[i])
21670         }
21671     },
21672     
21673     
21674     /**
21675      * cleanTableWidths.
21676      *
21677      * Quite often pasting from word etc.. results in tables with column and widths.
21678      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21679      *
21680      */
21681     cleanTableWidths : function(node)
21682     {
21683          
21684          
21685         if (!node) {
21686             this.cleanTableWidths(this.doc.body);
21687             return;
21688         }
21689         
21690         // ignore list...
21691         if (node.nodeName == "#text" || node.nodeName == "#comment") {
21692             return; 
21693         }
21694         Roo.log(node.tagName);
21695         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21696             this.iterateChildren(node, this.cleanTableWidths);
21697             return;
21698         }
21699         if (node.hasAttribute('width')) {
21700             node.removeAttribute('width');
21701         }
21702         
21703          
21704         if (node.hasAttribute("style")) {
21705             // pretty basic...
21706             
21707             var styles = node.getAttribute("style").split(";");
21708             var nstyle = [];
21709             Roo.each(styles, function(s) {
21710                 if (!s.match(/:/)) {
21711                     return;
21712                 }
21713                 var kv = s.split(":");
21714                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21715                     return;
21716                 }
21717                 // what ever is left... we allow.
21718                 nstyle.push(s);
21719             });
21720             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21721             if (!nstyle.length) {
21722                 node.removeAttribute('style');
21723             }
21724         }
21725         
21726         this.iterateChildren(node, this.cleanTableWidths);
21727         
21728         
21729     },
21730     
21731     
21732     
21733     
21734     domToHTML : function(currentElement, depth, nopadtext) {
21735         
21736         depth = depth || 0;
21737         nopadtext = nopadtext || false;
21738     
21739         if (!currentElement) {
21740             return this.domToHTML(this.doc.body);
21741         }
21742         
21743         //Roo.log(currentElement);
21744         var j;
21745         var allText = false;
21746         var nodeName = currentElement.nodeName;
21747         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21748         
21749         if  (nodeName == '#text') {
21750             
21751             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21752         }
21753         
21754         
21755         var ret = '';
21756         if (nodeName != 'BODY') {
21757              
21758             var i = 0;
21759             // Prints the node tagName, such as <A>, <IMG>, etc
21760             if (tagName) {
21761                 var attr = [];
21762                 for(i = 0; i < currentElement.attributes.length;i++) {
21763                     // quoting?
21764                     var aname = currentElement.attributes.item(i).name;
21765                     if (!currentElement.attributes.item(i).value.length) {
21766                         continue;
21767                     }
21768                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21769                 }
21770                 
21771                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21772             } 
21773             else {
21774                 
21775                 // eack
21776             }
21777         } else {
21778             tagName = false;
21779         }
21780         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21781             return ret;
21782         }
21783         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21784             nopadtext = true;
21785         }
21786         
21787         
21788         // Traverse the tree
21789         i = 0;
21790         var currentElementChild = currentElement.childNodes.item(i);
21791         var allText = true;
21792         var innerHTML  = '';
21793         lastnode = '';
21794         while (currentElementChild) {
21795             // Formatting code (indent the tree so it looks nice on the screen)
21796             var nopad = nopadtext;
21797             if (lastnode == 'SPAN') {
21798                 nopad  = true;
21799             }
21800             // text
21801             if  (currentElementChild.nodeName == '#text') {
21802                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21803                 toadd = nopadtext ? toadd : toadd.trim();
21804                 if (!nopad && toadd.length > 80) {
21805                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
21806                 }
21807                 innerHTML  += toadd;
21808                 
21809                 i++;
21810                 currentElementChild = currentElement.childNodes.item(i);
21811                 lastNode = '';
21812                 continue;
21813             }
21814             allText = false;
21815             
21816             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
21817                 
21818             // Recursively traverse the tree structure of the child node
21819             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
21820             lastnode = currentElementChild.nodeName;
21821             i++;
21822             currentElementChild=currentElement.childNodes.item(i);
21823         }
21824         
21825         ret += innerHTML;
21826         
21827         if (!allText) {
21828                 // The remaining code is mostly for formatting the tree
21829             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
21830         }
21831         
21832         
21833         if (tagName) {
21834             ret+= "</"+tagName+">";
21835         }
21836         return ret;
21837         
21838     },
21839         
21840     applyBlacklists : function()
21841     {
21842         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
21843         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
21844         
21845         this.white = [];
21846         this.black = [];
21847         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
21848             if (b.indexOf(tag) > -1) {
21849                 return;
21850             }
21851             this.white.push(tag);
21852             
21853         }, this);
21854         
21855         Roo.each(w, function(tag) {
21856             if (b.indexOf(tag) > -1) {
21857                 return;
21858             }
21859             if (this.white.indexOf(tag) > -1) {
21860                 return;
21861             }
21862             this.white.push(tag);
21863             
21864         }, this);
21865         
21866         
21867         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
21868             if (w.indexOf(tag) > -1) {
21869                 return;
21870             }
21871             this.black.push(tag);
21872             
21873         }, this);
21874         
21875         Roo.each(b, function(tag) {
21876             if (w.indexOf(tag) > -1) {
21877                 return;
21878             }
21879             if (this.black.indexOf(tag) > -1) {
21880                 return;
21881             }
21882             this.black.push(tag);
21883             
21884         }, this);
21885         
21886         
21887         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
21888         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
21889         
21890         this.cwhite = [];
21891         this.cblack = [];
21892         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
21893             if (b.indexOf(tag) > -1) {
21894                 return;
21895             }
21896             this.cwhite.push(tag);
21897             
21898         }, this);
21899         
21900         Roo.each(w, function(tag) {
21901             if (b.indexOf(tag) > -1) {
21902                 return;
21903             }
21904             if (this.cwhite.indexOf(tag) > -1) {
21905                 return;
21906             }
21907             this.cwhite.push(tag);
21908             
21909         }, this);
21910         
21911         
21912         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
21913             if (w.indexOf(tag) > -1) {
21914                 return;
21915             }
21916             this.cblack.push(tag);
21917             
21918         }, this);
21919         
21920         Roo.each(b, function(tag) {
21921             if (w.indexOf(tag) > -1) {
21922                 return;
21923             }
21924             if (this.cblack.indexOf(tag) > -1) {
21925                 return;
21926             }
21927             this.cblack.push(tag);
21928             
21929         }, this);
21930     },
21931     
21932     setStylesheets : function(stylesheets)
21933     {
21934         if(typeof(stylesheets) == 'string'){
21935             Roo.get(this.iframe.contentDocument.head).createChild({
21936                 tag : 'link',
21937                 rel : 'stylesheet',
21938                 type : 'text/css',
21939                 href : stylesheets
21940             });
21941             
21942             return;
21943         }
21944         var _this = this;
21945      
21946         Roo.each(stylesheets, function(s) {
21947             if(!s.length){
21948                 return;
21949             }
21950             
21951             Roo.get(_this.iframe.contentDocument.head).createChild({
21952                 tag : 'link',
21953                 rel : 'stylesheet',
21954                 type : 'text/css',
21955                 href : s
21956             });
21957         });
21958
21959         
21960     },
21961     
21962     removeStylesheets : function()
21963     {
21964         var _this = this;
21965         
21966         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
21967             s.remove();
21968         });
21969     },
21970     
21971     setStyle : function(style)
21972     {
21973         Roo.get(this.iframe.contentDocument.head).createChild({
21974             tag : 'style',
21975             type : 'text/css',
21976             html : style
21977         });
21978
21979         return;
21980     }
21981     
21982     // hide stuff that is not compatible
21983     /**
21984      * @event blur
21985      * @hide
21986      */
21987     /**
21988      * @event change
21989      * @hide
21990      */
21991     /**
21992      * @event focus
21993      * @hide
21994      */
21995     /**
21996      * @event specialkey
21997      * @hide
21998      */
21999     /**
22000      * @cfg {String} fieldClass @hide
22001      */
22002     /**
22003      * @cfg {String} focusClass @hide
22004      */
22005     /**
22006      * @cfg {String} autoCreate @hide
22007      */
22008     /**
22009      * @cfg {String} inputType @hide
22010      */
22011     /**
22012      * @cfg {String} invalidClass @hide
22013      */
22014     /**
22015      * @cfg {String} invalidText @hide
22016      */
22017     /**
22018      * @cfg {String} msgFx @hide
22019      */
22020     /**
22021      * @cfg {String} validateOnBlur @hide
22022      */
22023 });
22024
22025 Roo.HtmlEditorCore.white = [
22026         'area', 'br', 'img', 'input', 'hr', 'wbr',
22027         
22028        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
22029        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
22030        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
22031        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
22032        'table',   'ul',         'xmp', 
22033        
22034        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
22035       'thead',   'tr', 
22036      
22037       'dir', 'menu', 'ol', 'ul', 'dl',
22038        
22039       'embed',  'object'
22040 ];
22041
22042
22043 Roo.HtmlEditorCore.black = [
22044     //    'embed',  'object', // enable - backend responsiblity to clean thiese
22045         'applet', // 
22046         'base',   'basefont', 'bgsound', 'blink',  'body', 
22047         'frame',  'frameset', 'head',    'html',   'ilayer', 
22048         'iframe', 'layer',  'link',     'meta',    'object',   
22049         'script', 'style' ,'title',  'xml' // clean later..
22050 ];
22051 Roo.HtmlEditorCore.clean = [
22052     'script', 'style', 'title', 'xml'
22053 ];
22054 Roo.HtmlEditorCore.remove = [
22055     'font'
22056 ];
22057 // attributes..
22058
22059 Roo.HtmlEditorCore.ablack = [
22060     'on'
22061 ];
22062     
22063 Roo.HtmlEditorCore.aclean = [ 
22064     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22065 ];
22066
22067 // protocols..
22068 Roo.HtmlEditorCore.pwhite= [
22069         'http',  'https',  'mailto'
22070 ];
22071
22072 // white listed style attributes.
22073 Roo.HtmlEditorCore.cwhite= [
22074       //  'text-align', /// default is to allow most things..
22075       
22076          
22077 //        'font-size'//??
22078 ];
22079
22080 // black listed style attributes.
22081 Roo.HtmlEditorCore.cblack= [
22082       //  'font-size' -- this can be set by the project 
22083 ];
22084
22085
22086 Roo.HtmlEditorCore.swapCodes   =[ 
22087     [    8211, "--" ], 
22088     [    8212, "--" ], 
22089     [    8216,  "'" ],  
22090     [    8217, "'" ],  
22091     [    8220, '"' ],  
22092     [    8221, '"' ],  
22093     [    8226, "*" ],  
22094     [    8230, "..." ]
22095 ]; 
22096
22097     //<script type="text/javascript">
22098
22099 /*
22100  * Ext JS Library 1.1.1
22101  * Copyright(c) 2006-2007, Ext JS, LLC.
22102  * Licence LGPL
22103  * 
22104  */
22105  
22106  
22107 Roo.form.HtmlEditor = function(config){
22108     
22109     
22110     
22111     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
22112     
22113     if (!this.toolbars) {
22114         this.toolbars = [];
22115     }
22116     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22117     
22118     
22119 };
22120
22121 /**
22122  * @class Roo.form.HtmlEditor
22123  * @extends Roo.form.Field
22124  * Provides a lightweight HTML Editor component.
22125  *
22126  * This has been tested on Fireforx / Chrome.. IE may not be so great..
22127  * 
22128  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
22129  * supported by this editor.</b><br/><br/>
22130  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
22131  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22132  */
22133 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
22134     /**
22135      * @cfg {Boolean} clearUp
22136      */
22137     clearUp : true,
22138       /**
22139      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22140      */
22141     toolbars : false,
22142    
22143      /**
22144      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22145      *                        Roo.resizable.
22146      */
22147     resizable : false,
22148      /**
22149      * @cfg {Number} height (in pixels)
22150      */   
22151     height: 300,
22152    /**
22153      * @cfg {Number} width (in pixels)
22154      */   
22155     width: 500,
22156     
22157     /**
22158      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22159      * 
22160      */
22161     stylesheets: false,
22162     
22163     
22164      /**
22165      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
22166      * 
22167      */
22168     cblack: false,
22169     /**
22170      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
22171      * 
22172      */
22173     cwhite: false,
22174     
22175      /**
22176      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
22177      * 
22178      */
22179     black: false,
22180     /**
22181      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
22182      * 
22183      */
22184     white: false,
22185     
22186     // id of frame..
22187     frameId: false,
22188     
22189     // private properties
22190     validationEvent : false,
22191     deferHeight: true,
22192     initialized : false,
22193     activated : false,
22194     
22195     onFocus : Roo.emptyFn,
22196     iframePad:3,
22197     hideMode:'offsets',
22198     
22199     actionMode : 'container', // defaults to hiding it...
22200     
22201     defaultAutoCreate : { // modified by initCompnoent..
22202         tag: "textarea",
22203         style:"width:500px;height:300px;",
22204         autocomplete: "new-password"
22205     },
22206
22207     // private
22208     initComponent : function(){
22209         this.addEvents({
22210             /**
22211              * @event initialize
22212              * Fires when the editor is fully initialized (including the iframe)
22213              * @param {HtmlEditor} this
22214              */
22215             initialize: true,
22216             /**
22217              * @event activate
22218              * Fires when the editor is first receives the focus. Any insertion must wait
22219              * until after this event.
22220              * @param {HtmlEditor} this
22221              */
22222             activate: true,
22223              /**
22224              * @event beforesync
22225              * Fires before the textarea is updated with content from the editor iframe. Return false
22226              * to cancel the sync.
22227              * @param {HtmlEditor} this
22228              * @param {String} html
22229              */
22230             beforesync: true,
22231              /**
22232              * @event beforepush
22233              * Fires before the iframe editor is updated with content from the textarea. Return false
22234              * to cancel the push.
22235              * @param {HtmlEditor} this
22236              * @param {String} html
22237              */
22238             beforepush: true,
22239              /**
22240              * @event sync
22241              * Fires when the textarea is updated with content from the editor iframe.
22242              * @param {HtmlEditor} this
22243              * @param {String} html
22244              */
22245             sync: true,
22246              /**
22247              * @event push
22248              * Fires when the iframe editor is updated with content from the textarea.
22249              * @param {HtmlEditor} this
22250              * @param {String} html
22251              */
22252             push: true,
22253              /**
22254              * @event editmodechange
22255              * Fires when the editor switches edit modes
22256              * @param {HtmlEditor} this
22257              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22258              */
22259             editmodechange: true,
22260             /**
22261              * @event editorevent
22262              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22263              * @param {HtmlEditor} this
22264              */
22265             editorevent: true,
22266             /**
22267              * @event firstfocus
22268              * Fires when on first focus - needed by toolbars..
22269              * @param {HtmlEditor} this
22270              */
22271             firstfocus: true,
22272             /**
22273              * @event autosave
22274              * Auto save the htmlEditor value as a file into Events
22275              * @param {HtmlEditor} this
22276              */
22277             autosave: true,
22278             /**
22279              * @event savedpreview
22280              * preview the saved version of htmlEditor
22281              * @param {HtmlEditor} this
22282              */
22283             savedpreview: true,
22284             
22285             /**
22286             * @event stylesheetsclick
22287             * Fires when press the Sytlesheets button
22288             * @param {Roo.HtmlEditorCore} this
22289             */
22290             stylesheetsclick: true
22291         });
22292         this.defaultAutoCreate =  {
22293             tag: "textarea",
22294             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
22295             autocomplete: "new-password"
22296         };
22297     },
22298
22299     /**
22300      * Protected method that will not generally be called directly. It
22301      * is called when the editor creates its toolbar. Override this method if you need to
22302      * add custom toolbar buttons.
22303      * @param {HtmlEditor} editor
22304      */
22305     createToolbar : function(editor){
22306         Roo.log("create toolbars");
22307         if (!editor.toolbars || !editor.toolbars.length) {
22308             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
22309         }
22310         
22311         for (var i =0 ; i < editor.toolbars.length;i++) {
22312             editor.toolbars[i] = Roo.factory(
22313                     typeof(editor.toolbars[i]) == 'string' ?
22314                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
22315                 Roo.form.HtmlEditor);
22316             editor.toolbars[i].init(editor);
22317         }
22318          
22319         
22320     },
22321
22322      
22323     // private
22324     onRender : function(ct, position)
22325     {
22326         var _t = this;
22327         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
22328         
22329         this.wrap = this.el.wrap({
22330             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22331         });
22332         
22333         this.editorcore.onRender(ct, position);
22334          
22335         if (this.resizable) {
22336             this.resizeEl = new Roo.Resizable(this.wrap, {
22337                 pinned : true,
22338                 wrap: true,
22339                 dynamic : true,
22340                 minHeight : this.height,
22341                 height: this.height,
22342                 handles : this.resizable,
22343                 width: this.width,
22344                 listeners : {
22345                     resize : function(r, w, h) {
22346                         _t.onResize(w,h); // -something
22347                     }
22348                 }
22349             });
22350             
22351         }
22352         this.createToolbar(this);
22353        
22354         
22355         if(!this.width){
22356             this.setSize(this.wrap.getSize());
22357         }
22358         if (this.resizeEl) {
22359             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22360             // should trigger onReize..
22361         }
22362         
22363         this.keyNav = new Roo.KeyNav(this.el, {
22364             
22365             "tab" : function(e){
22366                 e.preventDefault();
22367                 
22368                 var value = this.getValue();
22369                 
22370                 var start = this.el.dom.selectionStart;
22371                 var end = this.el.dom.selectionEnd;
22372                 
22373                 if(!e.shiftKey){
22374                     
22375                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
22376                     this.el.dom.setSelectionRange(end + 1, end + 1);
22377                     return;
22378                 }
22379                 
22380                 var f = value.substring(0, start).split("\t");
22381                 
22382                 if(f.pop().length != 0){
22383                     return;
22384                 }
22385                 
22386                 this.setValue(f.join("\t") + value.substring(end));
22387                 this.el.dom.setSelectionRange(start - 1, start - 1);
22388                 
22389             },
22390             
22391             "home" : function(e){
22392                 e.preventDefault();
22393                 
22394                 var curr = this.el.dom.selectionStart;
22395                 var lines = this.getValue().split("\n");
22396                 
22397                 if(!lines.length){
22398                     return;
22399                 }
22400                 
22401                 if(e.ctrlKey){
22402                     this.el.dom.setSelectionRange(0, 0);
22403                     return;
22404                 }
22405                 
22406                 var pos = 0;
22407                 
22408                 for (var i = 0; i < lines.length;i++) {
22409                     pos += lines[i].length;
22410                     
22411                     if(i != 0){
22412                         pos += 1;
22413                     }
22414                     
22415                     if(pos < curr){
22416                         continue;
22417                     }
22418                     
22419                     pos -= lines[i].length;
22420                     
22421                     break;
22422                 }
22423                 
22424                 if(!e.shiftKey){
22425                     this.el.dom.setSelectionRange(pos, pos);
22426                     return;
22427                 }
22428                 
22429                 this.el.dom.selectionStart = pos;
22430                 this.el.dom.selectionEnd = curr;
22431             },
22432             
22433             "end" : function(e){
22434                 e.preventDefault();
22435                 
22436                 var curr = this.el.dom.selectionStart;
22437                 var lines = this.getValue().split("\n");
22438                 
22439                 if(!lines.length){
22440                     return;
22441                 }
22442                 
22443                 if(e.ctrlKey){
22444                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
22445                     return;
22446                 }
22447                 
22448                 var pos = 0;
22449                 
22450                 for (var i = 0; i < lines.length;i++) {
22451                     
22452                     pos += lines[i].length;
22453                     
22454                     if(i != 0){
22455                         pos += 1;
22456                     }
22457                     
22458                     if(pos < curr){
22459                         continue;
22460                     }
22461                     
22462                     break;
22463                 }
22464                 
22465                 if(!e.shiftKey){
22466                     this.el.dom.setSelectionRange(pos, pos);
22467                     return;
22468                 }
22469                 
22470                 this.el.dom.selectionStart = curr;
22471                 this.el.dom.selectionEnd = pos;
22472             },
22473
22474             scope : this,
22475
22476             doRelay : function(foo, bar, hname){
22477                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
22478             },
22479
22480             forceKeyDown: true
22481         });
22482         
22483 //        if(this.autosave && this.w){
22484 //            this.autoSaveFn = setInterval(this.autosave, 1000);
22485 //        }
22486     },
22487
22488     // private
22489     onResize : function(w, h)
22490     {
22491         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
22492         var ew = false;
22493         var eh = false;
22494         
22495         if(this.el ){
22496             if(typeof w == 'number'){
22497                 var aw = w - this.wrap.getFrameWidth('lr');
22498                 this.el.setWidth(this.adjustWidth('textarea', aw));
22499                 ew = aw;
22500             }
22501             if(typeof h == 'number'){
22502                 var tbh = 0;
22503                 for (var i =0; i < this.toolbars.length;i++) {
22504                     // fixme - ask toolbars for heights?
22505                     tbh += this.toolbars[i].tb.el.getHeight();
22506                     if (this.toolbars[i].footer) {
22507                         tbh += this.toolbars[i].footer.el.getHeight();
22508                     }
22509                 }
22510                 
22511                 
22512                 
22513                 
22514                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
22515                 ah -= 5; // knock a few pixes off for look..
22516 //                Roo.log(ah);
22517                 this.el.setHeight(this.adjustWidth('textarea', ah));
22518                 var eh = ah;
22519             }
22520         }
22521         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
22522         this.editorcore.onResize(ew,eh);
22523         
22524     },
22525
22526     /**
22527      * Toggles the editor between standard and source edit mode.
22528      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22529      */
22530     toggleSourceEdit : function(sourceEditMode)
22531     {
22532         this.editorcore.toggleSourceEdit(sourceEditMode);
22533         
22534         if(this.editorcore.sourceEditMode){
22535             Roo.log('editor - showing textarea');
22536             
22537 //            Roo.log('in');
22538 //            Roo.log(this.syncValue());
22539             this.editorcore.syncValue();
22540             this.el.removeClass('x-hidden');
22541             this.el.dom.removeAttribute('tabIndex');
22542             this.el.focus();
22543             
22544             for (var i = 0; i < this.toolbars.length; i++) {
22545                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
22546                     this.toolbars[i].tb.hide();
22547                     this.toolbars[i].footer.hide();
22548                 }
22549             }
22550             
22551         }else{
22552             Roo.log('editor - hiding textarea');
22553 //            Roo.log('out')
22554 //            Roo.log(this.pushValue()); 
22555             this.editorcore.pushValue();
22556             
22557             this.el.addClass('x-hidden');
22558             this.el.dom.setAttribute('tabIndex', -1);
22559             
22560             for (var i = 0; i < this.toolbars.length; i++) {
22561                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
22562                     this.toolbars[i].tb.show();
22563                     this.toolbars[i].footer.show();
22564                 }
22565             }
22566             
22567             //this.deferFocus();
22568         }
22569         
22570         this.setSize(this.wrap.getSize());
22571         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
22572         
22573         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
22574     },
22575  
22576     // private (for BoxComponent)
22577     adjustSize : Roo.BoxComponent.prototype.adjustSize,
22578
22579     // private (for BoxComponent)
22580     getResizeEl : function(){
22581         return this.wrap;
22582     },
22583
22584     // private (for BoxComponent)
22585     getPositionEl : function(){
22586         return this.wrap;
22587     },
22588
22589     // private
22590     initEvents : function(){
22591         this.originalValue = this.getValue();
22592     },
22593
22594     /**
22595      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22596      * @method
22597      */
22598     markInvalid : Roo.emptyFn,
22599     /**
22600      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22601      * @method
22602      */
22603     clearInvalid : Roo.emptyFn,
22604
22605     setValue : function(v){
22606         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
22607         this.editorcore.pushValue();
22608     },
22609
22610      
22611     // private
22612     deferFocus : function(){
22613         this.focus.defer(10, this);
22614     },
22615
22616     // doc'ed in Field
22617     focus : function(){
22618         this.editorcore.focus();
22619         
22620     },
22621       
22622
22623     // private
22624     onDestroy : function(){
22625         
22626         
22627         
22628         if(this.rendered){
22629             
22630             for (var i =0; i < this.toolbars.length;i++) {
22631                 // fixme - ask toolbars for heights?
22632                 this.toolbars[i].onDestroy();
22633             }
22634             
22635             this.wrap.dom.innerHTML = '';
22636             this.wrap.remove();
22637         }
22638     },
22639
22640     // private
22641     onFirstFocus : function(){
22642         //Roo.log("onFirstFocus");
22643         this.editorcore.onFirstFocus();
22644          for (var i =0; i < this.toolbars.length;i++) {
22645             this.toolbars[i].onFirstFocus();
22646         }
22647         
22648     },
22649     
22650     // private
22651     syncValue : function()
22652     {
22653         this.editorcore.syncValue();
22654     },
22655     
22656     pushValue : function()
22657     {
22658         this.editorcore.pushValue();
22659     },
22660     
22661     setStylesheets : function(stylesheets)
22662     {
22663         this.editorcore.setStylesheets(stylesheets);
22664     },
22665     
22666     removeStylesheets : function()
22667     {
22668         this.editorcore.removeStylesheets();
22669     }
22670      
22671     
22672     // hide stuff that is not compatible
22673     /**
22674      * @event blur
22675      * @hide
22676      */
22677     /**
22678      * @event change
22679      * @hide
22680      */
22681     /**
22682      * @event focus
22683      * @hide
22684      */
22685     /**
22686      * @event specialkey
22687      * @hide
22688      */
22689     /**
22690      * @cfg {String} fieldClass @hide
22691      */
22692     /**
22693      * @cfg {String} focusClass @hide
22694      */
22695     /**
22696      * @cfg {String} autoCreate @hide
22697      */
22698     /**
22699      * @cfg {String} inputType @hide
22700      */
22701     /**
22702      * @cfg {String} invalidClass @hide
22703      */
22704     /**
22705      * @cfg {String} invalidText @hide
22706      */
22707     /**
22708      * @cfg {String} msgFx @hide
22709      */
22710     /**
22711      * @cfg {String} validateOnBlur @hide
22712      */
22713 });
22714  
22715     // <script type="text/javascript">
22716 /*
22717  * Based on
22718  * Ext JS Library 1.1.1
22719  * Copyright(c) 2006-2007, Ext JS, LLC.
22720  *  
22721  
22722  */
22723
22724 /**
22725  * @class Roo.form.HtmlEditorToolbar1
22726  * Basic Toolbar
22727  * 
22728  * Usage:
22729  *
22730  new Roo.form.HtmlEditor({
22731     ....
22732     toolbars : [
22733         new Roo.form.HtmlEditorToolbar1({
22734             disable : { fonts: 1 , format: 1, ..., ... , ...],
22735             btns : [ .... ]
22736         })
22737     }
22738      
22739  * 
22740  * @cfg {Object} disable List of elements to disable..
22741  * @cfg {Array} btns List of additional buttons.
22742  * 
22743  * 
22744  * NEEDS Extra CSS? 
22745  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22746  */
22747  
22748 Roo.form.HtmlEditor.ToolbarStandard = function(config)
22749 {
22750     
22751     Roo.apply(this, config);
22752     
22753     // default disabled, based on 'good practice'..
22754     this.disable = this.disable || {};
22755     Roo.applyIf(this.disable, {
22756         fontSize : true,
22757         colors : true,
22758         specialElements : true
22759     });
22760     
22761     
22762     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22763     // dont call parent... till later.
22764 }
22765
22766 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
22767     
22768     tb: false,
22769     
22770     rendered: false,
22771     
22772     editor : false,
22773     editorcore : false,
22774     /**
22775      * @cfg {Object} disable  List of toolbar elements to disable
22776          
22777      */
22778     disable : false,
22779     
22780     
22781      /**
22782      * @cfg {String} createLinkText The default text for the create link prompt
22783      */
22784     createLinkText : 'Please enter the URL for the link:',
22785     /**
22786      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
22787      */
22788     defaultLinkValue : 'http:/'+'/',
22789    
22790     
22791       /**
22792      * @cfg {Array} fontFamilies An array of available font families
22793      */
22794     fontFamilies : [
22795         'Arial',
22796         'Courier New',
22797         'Tahoma',
22798         'Times New Roman',
22799         'Verdana'
22800     ],
22801     
22802     specialChars : [
22803            "&#169;",
22804           "&#174;",     
22805           "&#8482;",    
22806           "&#163;" ,    
22807          // "&#8212;",    
22808           "&#8230;",    
22809           "&#247;" ,    
22810         //  "&#225;" ,     ?? a acute?
22811            "&#8364;"    , //Euro
22812        //   "&#8220;"    ,
22813         //  "&#8221;"    ,
22814         //  "&#8226;"    ,
22815           "&#176;"  //   , // degrees
22816
22817          // "&#233;"     , // e ecute
22818          // "&#250;"     , // u ecute?
22819     ],
22820     
22821     specialElements : [
22822         {
22823             text: "Insert Table",
22824             xtype: 'MenuItem',
22825             xns : Roo.Menu,
22826             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
22827                 
22828         },
22829         {    
22830             text: "Insert Image",
22831             xtype: 'MenuItem',
22832             xns : Roo.Menu,
22833             ihtml : '<img src="about:blank"/>'
22834             
22835         }
22836         
22837          
22838     ],
22839     
22840     
22841     inputElements : [ 
22842             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
22843             "input:submit", "input:button", "select", "textarea", "label" ],
22844     formats : [
22845         ["p"] ,  
22846         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
22847         ["pre"],[ "code"], 
22848         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
22849         ['div'],['span']
22850     ],
22851     
22852     cleanStyles : [
22853         "font-size"
22854     ],
22855      /**
22856      * @cfg {String} defaultFont default font to use.
22857      */
22858     defaultFont: 'tahoma',
22859    
22860     fontSelect : false,
22861     
22862     
22863     formatCombo : false,
22864     
22865     init : function(editor)
22866     {
22867         this.editor = editor;
22868         this.editorcore = editor.editorcore ? editor.editorcore : editor;
22869         var editorcore = this.editorcore;
22870         
22871         var _t = this;
22872         
22873         var fid = editorcore.frameId;
22874         var etb = this;
22875         function btn(id, toggle, handler){
22876             var xid = fid + '-'+ id ;
22877             return {
22878                 id : xid,
22879                 cmd : id,
22880                 cls : 'x-btn-icon x-edit-'+id,
22881                 enableToggle:toggle !== false,
22882                 scope: _t, // was editor...
22883                 handler:handler||_t.relayBtnCmd,
22884                 clickEvent:'mousedown',
22885                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
22886                 tabIndex:-1
22887             };
22888         }
22889         
22890         
22891         
22892         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
22893         this.tb = tb;
22894          // stop form submits
22895         tb.el.on('click', function(e){
22896             e.preventDefault(); // what does this do?
22897         });
22898
22899         if(!this.disable.font) { // && !Roo.isSafari){
22900             /* why no safari for fonts 
22901             editor.fontSelect = tb.el.createChild({
22902                 tag:'select',
22903                 tabIndex: -1,
22904                 cls:'x-font-select',
22905                 html: this.createFontOptions()
22906             });
22907             
22908             editor.fontSelect.on('change', function(){
22909                 var font = editor.fontSelect.dom.value;
22910                 editor.relayCmd('fontname', font);
22911                 editor.deferFocus();
22912             }, editor);
22913             
22914             tb.add(
22915                 editor.fontSelect.dom,
22916                 '-'
22917             );
22918             */
22919             
22920         };
22921         if(!this.disable.formats){
22922             this.formatCombo = new Roo.form.ComboBox({
22923                 store: new Roo.data.SimpleStore({
22924                     id : 'tag',
22925                     fields: ['tag'],
22926                     data : this.formats // from states.js
22927                 }),
22928                 blockFocus : true,
22929                 name : '',
22930                 //autoCreate : {tag: "div",  size: "20"},
22931                 displayField:'tag',
22932                 typeAhead: false,
22933                 mode: 'local',
22934                 editable : false,
22935                 triggerAction: 'all',
22936                 emptyText:'Add tag',
22937                 selectOnFocus:true,
22938                 width:135,
22939                 listeners : {
22940                     'select': function(c, r, i) {
22941                         editorcore.insertTag(r.get('tag'));
22942                         editor.focus();
22943                     }
22944                 }
22945
22946             });
22947             tb.addField(this.formatCombo);
22948             
22949         }
22950         
22951         if(!this.disable.format){
22952             tb.add(
22953                 btn('bold'),
22954                 btn('italic'),
22955                 btn('underline'),
22956                 btn('strikethrough')
22957             );
22958         };
22959         if(!this.disable.fontSize){
22960             tb.add(
22961                 '-',
22962                 
22963                 
22964                 btn('increasefontsize', false, editorcore.adjustFont),
22965                 btn('decreasefontsize', false, editorcore.adjustFont)
22966             );
22967         };
22968         
22969         
22970         if(!this.disable.colors){
22971             tb.add(
22972                 '-', {
22973                     id:editorcore.frameId +'-forecolor',
22974                     cls:'x-btn-icon x-edit-forecolor',
22975                     clickEvent:'mousedown',
22976                     tooltip: this.buttonTips['forecolor'] || undefined,
22977                     tabIndex:-1,
22978                     menu : new Roo.menu.ColorMenu({
22979                         allowReselect: true,
22980                         focus: Roo.emptyFn,
22981                         value:'000000',
22982                         plain:true,
22983                         selectHandler: function(cp, color){
22984                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
22985                             editor.deferFocus();
22986                         },
22987                         scope: editorcore,
22988                         clickEvent:'mousedown'
22989                     })
22990                 }, {
22991                     id:editorcore.frameId +'backcolor',
22992                     cls:'x-btn-icon x-edit-backcolor',
22993                     clickEvent:'mousedown',
22994                     tooltip: this.buttonTips['backcolor'] || undefined,
22995                     tabIndex:-1,
22996                     menu : new Roo.menu.ColorMenu({
22997                         focus: Roo.emptyFn,
22998                         value:'FFFFFF',
22999                         plain:true,
23000                         allowReselect: true,
23001                         selectHandler: function(cp, color){
23002                             if(Roo.isGecko){
23003                                 editorcore.execCmd('useCSS', false);
23004                                 editorcore.execCmd('hilitecolor', color);
23005                                 editorcore.execCmd('useCSS', true);
23006                                 editor.deferFocus();
23007                             }else{
23008                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
23009                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
23010                                 editor.deferFocus();
23011                             }
23012                         },
23013                         scope:editorcore,
23014                         clickEvent:'mousedown'
23015                     })
23016                 }
23017             );
23018         };
23019         // now add all the items...
23020         
23021
23022         if(!this.disable.alignments){
23023             tb.add(
23024                 '-',
23025                 btn('justifyleft'),
23026                 btn('justifycenter'),
23027                 btn('justifyright')
23028             );
23029         };
23030
23031         //if(!Roo.isSafari){
23032             if(!this.disable.links){
23033                 tb.add(
23034                     '-',
23035                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
23036                 );
23037             };
23038
23039             if(!this.disable.lists){
23040                 tb.add(
23041                     '-',
23042                     btn('insertorderedlist'),
23043                     btn('insertunorderedlist')
23044                 );
23045             }
23046             if(!this.disable.sourceEdit){
23047                 tb.add(
23048                     '-',
23049                     btn('sourceedit', true, function(btn){
23050                         this.toggleSourceEdit(btn.pressed);
23051                     })
23052                 );
23053             }
23054         //}
23055         
23056         var smenu = { };
23057         // special menu.. - needs to be tidied up..
23058         if (!this.disable.special) {
23059             smenu = {
23060                 text: "&#169;",
23061                 cls: 'x-edit-none',
23062                 
23063                 menu : {
23064                     items : []
23065                 }
23066             };
23067             for (var i =0; i < this.specialChars.length; i++) {
23068                 smenu.menu.items.push({
23069                     
23070                     html: this.specialChars[i],
23071                     handler: function(a,b) {
23072                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
23073                         //editor.insertAtCursor(a.html);
23074                         
23075                     },
23076                     tabIndex:-1
23077                 });
23078             }
23079             
23080             
23081             tb.add(smenu);
23082             
23083             
23084         }
23085         
23086         var cmenu = { };
23087         if (!this.disable.cleanStyles) {
23088             cmenu = {
23089                 cls: 'x-btn-icon x-btn-clear',
23090                 
23091                 menu : {
23092                     items : []
23093                 }
23094             };
23095             for (var i =0; i < this.cleanStyles.length; i++) {
23096                 cmenu.menu.items.push({
23097                     actiontype : this.cleanStyles[i],
23098                     html: 'Remove ' + this.cleanStyles[i],
23099                     handler: function(a,b) {
23100 //                        Roo.log(a);
23101 //                        Roo.log(b);
23102                         var c = Roo.get(editorcore.doc.body);
23103                         c.select('[style]').each(function(s) {
23104                             s.dom.style.removeProperty(a.actiontype);
23105                         });
23106                         editorcore.syncValue();
23107                     },
23108                     tabIndex:-1
23109                 });
23110             }
23111              cmenu.menu.items.push({
23112                 actiontype : 'tablewidths',
23113                 html: 'Remove Table Widths',
23114                 handler: function(a,b) {
23115                     editorcore.cleanTableWidths();
23116                     editorcore.syncValue();
23117                 },
23118                 tabIndex:-1
23119             });
23120             cmenu.menu.items.push({
23121                 actiontype : 'word',
23122                 html: 'Remove MS Word Formating',
23123                 handler: function(a,b) {
23124                     editorcore.cleanWord();
23125                     editorcore.syncValue();
23126                 },
23127                 tabIndex:-1
23128             });
23129             
23130             cmenu.menu.items.push({
23131                 actiontype : 'all',
23132                 html: 'Remove All Styles',
23133                 handler: function(a,b) {
23134                     
23135                     var c = Roo.get(editorcore.doc.body);
23136                     c.select('[style]').each(function(s) {
23137                         s.dom.removeAttribute('style');
23138                     });
23139                     editorcore.syncValue();
23140                 },
23141                 tabIndex:-1
23142             });
23143             
23144             cmenu.menu.items.push({
23145                 actiontype : 'all',
23146                 html: 'Remove All CSS Classes',
23147                 handler: function(a,b) {
23148                     
23149                     var c = Roo.get(editorcore.doc.body);
23150                     c.select('[class]').each(function(s) {
23151                         s.dom.className = '';
23152                     });
23153                     editorcore.syncValue();
23154                 },
23155                 tabIndex:-1
23156             });
23157             
23158              cmenu.menu.items.push({
23159                 actiontype : 'tidy',
23160                 html: 'Tidy HTML Source',
23161                 handler: function(a,b) {
23162                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
23163                     editorcore.syncValue();
23164                 },
23165                 tabIndex:-1
23166             });
23167             
23168             
23169             tb.add(cmenu);
23170         }
23171          
23172         if (!this.disable.specialElements) {
23173             var semenu = {
23174                 text: "Other;",
23175                 cls: 'x-edit-none',
23176                 menu : {
23177                     items : []
23178                 }
23179             };
23180             for (var i =0; i < this.specialElements.length; i++) {
23181                 semenu.menu.items.push(
23182                     Roo.apply({ 
23183                         handler: function(a,b) {
23184                             editor.insertAtCursor(this.ihtml);
23185                         }
23186                     }, this.specialElements[i])
23187                 );
23188                     
23189             }
23190             
23191             tb.add(semenu);
23192             
23193             
23194         }
23195          
23196         
23197         if (this.btns) {
23198             for(var i =0; i< this.btns.length;i++) {
23199                 var b = Roo.factory(this.btns[i],Roo.form);
23200                 b.cls =  'x-edit-none';
23201                 
23202                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
23203                     b.cls += ' x-init-enable';
23204                 }
23205                 
23206                 b.scope = editorcore;
23207                 tb.add(b);
23208             }
23209         
23210         }
23211         
23212         
23213         
23214         // disable everything...
23215         
23216         this.tb.items.each(function(item){
23217             
23218            if(
23219                 item.id != editorcore.frameId+ '-sourceedit' && 
23220                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
23221             ){
23222                 
23223                 item.disable();
23224             }
23225         });
23226         this.rendered = true;
23227         
23228         // the all the btns;
23229         editor.on('editorevent', this.updateToolbar, this);
23230         // other toolbars need to implement this..
23231         //editor.on('editmodechange', this.updateToolbar, this);
23232     },
23233     
23234     
23235     relayBtnCmd : function(btn) {
23236         this.editorcore.relayCmd(btn.cmd);
23237     },
23238     // private used internally
23239     createLink : function(){
23240         Roo.log("create link?");
23241         var url = prompt(this.createLinkText, this.defaultLinkValue);
23242         if(url && url != 'http:/'+'/'){
23243             this.editorcore.relayCmd('createlink', url);
23244         }
23245     },
23246
23247     
23248     /**
23249      * Protected method that will not generally be called directly. It triggers
23250      * a toolbar update by reading the markup state of the current selection in the editor.
23251      */
23252     updateToolbar: function(){
23253
23254         if(!this.editorcore.activated){
23255             this.editor.onFirstFocus();
23256             return;
23257         }
23258
23259         var btns = this.tb.items.map, 
23260             doc = this.editorcore.doc,
23261             frameId = this.editorcore.frameId;
23262
23263         if(!this.disable.font && !Roo.isSafari){
23264             /*
23265             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
23266             if(name != this.fontSelect.dom.value){
23267                 this.fontSelect.dom.value = name;
23268             }
23269             */
23270         }
23271         if(!this.disable.format){
23272             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
23273             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
23274             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
23275             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
23276         }
23277         if(!this.disable.alignments){
23278             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
23279             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
23280             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
23281         }
23282         if(!Roo.isSafari && !this.disable.lists){
23283             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
23284             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
23285         }
23286         
23287         var ans = this.editorcore.getAllAncestors();
23288         if (this.formatCombo) {
23289             
23290             
23291             var store = this.formatCombo.store;
23292             this.formatCombo.setValue("");
23293             for (var i =0; i < ans.length;i++) {
23294                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23295                     // select it..
23296                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23297                     break;
23298                 }
23299             }
23300         }
23301         
23302         
23303         
23304         // hides menus... - so this cant be on a menu...
23305         Roo.menu.MenuMgr.hideAll();
23306
23307         //this.editorsyncValue();
23308     },
23309    
23310     
23311     createFontOptions : function(){
23312         var buf = [], fs = this.fontFamilies, ff, lc;
23313         
23314         
23315         
23316         for(var i = 0, len = fs.length; i< len; i++){
23317             ff = fs[i];
23318             lc = ff.toLowerCase();
23319             buf.push(
23320                 '<option value="',lc,'" style="font-family:',ff,';"',
23321                     (this.defaultFont == lc ? ' selected="true">' : '>'),
23322                     ff,
23323                 '</option>'
23324             );
23325         }
23326         return buf.join('');
23327     },
23328     
23329     toggleSourceEdit : function(sourceEditMode){
23330         
23331         Roo.log("toolbar toogle");
23332         if(sourceEditMode === undefined){
23333             sourceEditMode = !this.sourceEditMode;
23334         }
23335         this.sourceEditMode = sourceEditMode === true;
23336         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
23337         // just toggle the button?
23338         if(btn.pressed !== this.sourceEditMode){
23339             btn.toggle(this.sourceEditMode);
23340             return;
23341         }
23342         
23343         if(sourceEditMode){
23344             Roo.log("disabling buttons");
23345             this.tb.items.each(function(item){
23346                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
23347                     item.disable();
23348                 }
23349             });
23350           
23351         }else{
23352             Roo.log("enabling buttons");
23353             if(this.editorcore.initialized){
23354                 this.tb.items.each(function(item){
23355                     item.enable();
23356                 });
23357             }
23358             
23359         }
23360         Roo.log("calling toggole on editor");
23361         // tell the editor that it's been pressed..
23362         this.editor.toggleSourceEdit(sourceEditMode);
23363        
23364     },
23365      /**
23366      * Object collection of toolbar tooltips for the buttons in the editor. The key
23367      * is the command id associated with that button and the value is a valid QuickTips object.
23368      * For example:
23369 <pre><code>
23370 {
23371     bold : {
23372         title: 'Bold (Ctrl+B)',
23373         text: 'Make the selected text bold.',
23374         cls: 'x-html-editor-tip'
23375     },
23376     italic : {
23377         title: 'Italic (Ctrl+I)',
23378         text: 'Make the selected text italic.',
23379         cls: 'x-html-editor-tip'
23380     },
23381     ...
23382 </code></pre>
23383     * @type Object
23384      */
23385     buttonTips : {
23386         bold : {
23387             title: 'Bold (Ctrl+B)',
23388             text: 'Make the selected text bold.',
23389             cls: 'x-html-editor-tip'
23390         },
23391         italic : {
23392             title: 'Italic (Ctrl+I)',
23393             text: 'Make the selected text italic.',
23394             cls: 'x-html-editor-tip'
23395         },
23396         underline : {
23397             title: 'Underline (Ctrl+U)',
23398             text: 'Underline the selected text.',
23399             cls: 'x-html-editor-tip'
23400         },
23401         strikethrough : {
23402             title: 'Strikethrough',
23403             text: 'Strikethrough the selected text.',
23404             cls: 'x-html-editor-tip'
23405         },
23406         increasefontsize : {
23407             title: 'Grow Text',
23408             text: 'Increase the font size.',
23409             cls: 'x-html-editor-tip'
23410         },
23411         decreasefontsize : {
23412             title: 'Shrink Text',
23413             text: 'Decrease the font size.',
23414             cls: 'x-html-editor-tip'
23415         },
23416         backcolor : {
23417             title: 'Text Highlight Color',
23418             text: 'Change the background color of the selected text.',
23419             cls: 'x-html-editor-tip'
23420         },
23421         forecolor : {
23422             title: 'Font Color',
23423             text: 'Change the color of the selected text.',
23424             cls: 'x-html-editor-tip'
23425         },
23426         justifyleft : {
23427             title: 'Align Text Left',
23428             text: 'Align text to the left.',
23429             cls: 'x-html-editor-tip'
23430         },
23431         justifycenter : {
23432             title: 'Center Text',
23433             text: 'Center text in the editor.',
23434             cls: 'x-html-editor-tip'
23435         },
23436         justifyright : {
23437             title: 'Align Text Right',
23438             text: 'Align text to the right.',
23439             cls: 'x-html-editor-tip'
23440         },
23441         insertunorderedlist : {
23442             title: 'Bullet List',
23443             text: 'Start a bulleted list.',
23444             cls: 'x-html-editor-tip'
23445         },
23446         insertorderedlist : {
23447             title: 'Numbered List',
23448             text: 'Start a numbered list.',
23449             cls: 'x-html-editor-tip'
23450         },
23451         createlink : {
23452             title: 'Hyperlink',
23453             text: 'Make the selected text a hyperlink.',
23454             cls: 'x-html-editor-tip'
23455         },
23456         sourceedit : {
23457             title: 'Source Edit',
23458             text: 'Switch to source editing mode.',
23459             cls: 'x-html-editor-tip'
23460         }
23461     },
23462     // private
23463     onDestroy : function(){
23464         if(this.rendered){
23465             
23466             this.tb.items.each(function(item){
23467                 if(item.menu){
23468                     item.menu.removeAll();
23469                     if(item.menu.el){
23470                         item.menu.el.destroy();
23471                     }
23472                 }
23473                 item.destroy();
23474             });
23475              
23476         }
23477     },
23478     onFirstFocus: function() {
23479         this.tb.items.each(function(item){
23480            item.enable();
23481         });
23482     }
23483 });
23484
23485
23486
23487
23488 // <script type="text/javascript">
23489 /*
23490  * Based on
23491  * Ext JS Library 1.1.1
23492  * Copyright(c) 2006-2007, Ext JS, LLC.
23493  *  
23494  
23495  */
23496
23497  
23498 /**
23499  * @class Roo.form.HtmlEditor.ToolbarContext
23500  * Context Toolbar
23501  * 
23502  * Usage:
23503  *
23504  new Roo.form.HtmlEditor({
23505     ....
23506     toolbars : [
23507         { xtype: 'ToolbarStandard', styles : {} }
23508         { xtype: 'ToolbarContext', disable : {} }
23509     ]
23510 })
23511
23512      
23513  * 
23514  * @config : {Object} disable List of elements to disable.. (not done yet.)
23515  * @config : {Object} styles  Map of styles available.
23516  * 
23517  */
23518
23519 Roo.form.HtmlEditor.ToolbarContext = function(config)
23520 {
23521     
23522     Roo.apply(this, config);
23523     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23524     // dont call parent... till later.
23525     this.styles = this.styles || {};
23526 }
23527
23528  
23529
23530 Roo.form.HtmlEditor.ToolbarContext.types = {
23531     'IMG' : {
23532         width : {
23533             title: "Width",
23534             width: 40
23535         },
23536         height:  {
23537             title: "Height",
23538             width: 40
23539         },
23540         align: {
23541             title: "Align",
23542             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
23543             width : 80
23544             
23545         },
23546         border: {
23547             title: "Border",
23548             width: 40
23549         },
23550         alt: {
23551             title: "Alt",
23552             width: 120
23553         },
23554         src : {
23555             title: "Src",
23556             width: 220
23557         }
23558         
23559     },
23560     'A' : {
23561         name : {
23562             title: "Name",
23563             width: 50
23564         },
23565         target:  {
23566             title: "Target",
23567             width: 120
23568         },
23569         href:  {
23570             title: "Href",
23571             width: 220
23572         } // border?
23573         
23574     },
23575     'TABLE' : {
23576         rows : {
23577             title: "Rows",
23578             width: 20
23579         },
23580         cols : {
23581             title: "Cols",
23582             width: 20
23583         },
23584         width : {
23585             title: "Width",
23586             width: 40
23587         },
23588         height : {
23589             title: "Height",
23590             width: 40
23591         },
23592         border : {
23593             title: "Border",
23594             width: 20
23595         }
23596     },
23597     'TD' : {
23598         width : {
23599             title: "Width",
23600             width: 40
23601         },
23602         height : {
23603             title: "Height",
23604             width: 40
23605         },   
23606         align: {
23607             title: "Align",
23608             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
23609             width: 80
23610         },
23611         valign: {
23612             title: "Valign",
23613             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
23614             width: 80
23615         },
23616         colspan: {
23617             title: "Colspan",
23618             width: 20
23619             
23620         },
23621          'font-family'  : {
23622             title : "Font",
23623             style : 'fontFamily',
23624             displayField: 'display',
23625             optname : 'font-family',
23626             width: 140
23627         }
23628     },
23629     'INPUT' : {
23630         name : {
23631             title: "name",
23632             width: 120
23633         },
23634         value : {
23635             title: "Value",
23636             width: 120
23637         },
23638         width : {
23639             title: "Width",
23640             width: 40
23641         }
23642     },
23643     'LABEL' : {
23644         'for' : {
23645             title: "For",
23646             width: 120
23647         }
23648     },
23649     'TEXTAREA' : {
23650           name : {
23651             title: "name",
23652             width: 120
23653         },
23654         rows : {
23655             title: "Rows",
23656             width: 20
23657         },
23658         cols : {
23659             title: "Cols",
23660             width: 20
23661         }
23662     },
23663     'SELECT' : {
23664         name : {
23665             title: "name",
23666             width: 120
23667         },
23668         selectoptions : {
23669             title: "Options",
23670             width: 200
23671         }
23672     },
23673     
23674     // should we really allow this??
23675     // should this just be 
23676     'BODY' : {
23677         title : {
23678             title: "Title",
23679             width: 200,
23680             disabled : true
23681         }
23682     },
23683     'SPAN' : {
23684         'font-family'  : {
23685             title : "Font",
23686             style : 'fontFamily',
23687             displayField: 'display',
23688             optname : 'font-family',
23689             width: 140
23690         }
23691     },
23692     'DIV' : {
23693         'font-family'  : {
23694             title : "Font",
23695             style : 'fontFamily',
23696             displayField: 'display',
23697             optname : 'font-family',
23698             width: 140
23699         }
23700     },
23701      'P' : {
23702         'font-family'  : {
23703             title : "Font",
23704             style : 'fontFamily',
23705             displayField: 'display',
23706             optname : 'font-family',
23707             width: 140
23708         }
23709     },
23710     
23711     '*' : {
23712         // empty..
23713     }
23714
23715 };
23716
23717 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
23718 Roo.form.HtmlEditor.ToolbarContext.stores = false;
23719
23720 Roo.form.HtmlEditor.ToolbarContext.options = {
23721         'font-family'  : [ 
23722                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
23723                 [ 'Courier New', 'Courier New'],
23724                 [ 'Tahoma', 'Tahoma'],
23725                 [ 'Times New Roman,serif', 'Times'],
23726                 [ 'Verdana','Verdana' ]
23727         ]
23728 };
23729
23730 // fixme - these need to be configurable..
23731  
23732
23733 //Roo.form.HtmlEditor.ToolbarContext.types
23734
23735
23736 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
23737     
23738     tb: false,
23739     
23740     rendered: false,
23741     
23742     editor : false,
23743     editorcore : false,
23744     /**
23745      * @cfg {Object} disable  List of toolbar elements to disable
23746          
23747      */
23748     disable : false,
23749     /**
23750      * @cfg {Object} styles List of styles 
23751      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
23752      *
23753      * These must be defined in the page, so they get rendered correctly..
23754      * .headline { }
23755      * TD.underline { }
23756      * 
23757      */
23758     styles : false,
23759     
23760     options: false,
23761     
23762     toolbars : false,
23763     
23764     init : function(editor)
23765     {
23766         this.editor = editor;
23767         this.editorcore = editor.editorcore ? editor.editorcore : editor;
23768         var editorcore = this.editorcore;
23769         
23770         var fid = editorcore.frameId;
23771         var etb = this;
23772         function btn(id, toggle, handler){
23773             var xid = fid + '-'+ id ;
23774             return {
23775                 id : xid,
23776                 cmd : id,
23777                 cls : 'x-btn-icon x-edit-'+id,
23778                 enableToggle:toggle !== false,
23779                 scope: editorcore, // was editor...
23780                 handler:handler||editorcore.relayBtnCmd,
23781                 clickEvent:'mousedown',
23782                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
23783                 tabIndex:-1
23784             };
23785         }
23786         // create a new element.
23787         var wdiv = editor.wrap.createChild({
23788                 tag: 'div'
23789             }, editor.wrap.dom.firstChild.nextSibling, true);
23790         
23791         // can we do this more than once??
23792         
23793          // stop form submits
23794       
23795  
23796         // disable everything...
23797         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
23798         this.toolbars = {};
23799            
23800         for (var i in  ty) {
23801           
23802             this.toolbars[i] = this.buildToolbar(ty[i],i);
23803         }
23804         this.tb = this.toolbars.BODY;
23805         this.tb.el.show();
23806         this.buildFooter();
23807         this.footer.show();
23808         editor.on('hide', function( ) { this.footer.hide() }, this);
23809         editor.on('show', function( ) { this.footer.show() }, this);
23810         
23811          
23812         this.rendered = true;
23813         
23814         // the all the btns;
23815         editor.on('editorevent', this.updateToolbar, this);
23816         // other toolbars need to implement this..
23817         //editor.on('editmodechange', this.updateToolbar, this);
23818     },
23819     
23820     
23821     
23822     /**
23823      * Protected method that will not generally be called directly. It triggers
23824      * a toolbar update by reading the markup state of the current selection in the editor.
23825      *
23826      * Note you can force an update by calling on('editorevent', scope, false)
23827      */
23828     updateToolbar: function(editor,ev,sel){
23829
23830         //Roo.log(ev);
23831         // capture mouse up - this is handy for selecting images..
23832         // perhaps should go somewhere else...
23833         if(!this.editorcore.activated){
23834              this.editor.onFirstFocus();
23835             return;
23836         }
23837         
23838         
23839         
23840         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
23841         // selectNode - might want to handle IE?
23842         if (ev &&
23843             (ev.type == 'mouseup' || ev.type == 'click' ) &&
23844             ev.target && ev.target.tagName == 'IMG') {
23845             // they have click on an image...
23846             // let's see if we can change the selection...
23847             sel = ev.target;
23848          
23849               var nodeRange = sel.ownerDocument.createRange();
23850             try {
23851                 nodeRange.selectNode(sel);
23852             } catch (e) {
23853                 nodeRange.selectNodeContents(sel);
23854             }
23855             //nodeRange.collapse(true);
23856             var s = this.editorcore.win.getSelection();
23857             s.removeAllRanges();
23858             s.addRange(nodeRange);
23859         }  
23860         
23861       
23862         var updateFooter = sel ? false : true;
23863         
23864         
23865         var ans = this.editorcore.getAllAncestors();
23866         
23867         // pick
23868         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
23869         
23870         if (!sel) { 
23871             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
23872             sel = sel ? sel : this.editorcore.doc.body;
23873             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
23874             
23875         }
23876         // pick a menu that exists..
23877         var tn = sel.tagName.toUpperCase();
23878         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
23879         
23880         tn = sel.tagName.toUpperCase();
23881         
23882         var lastSel = this.tb.selectedNode;
23883         
23884         this.tb.selectedNode = sel;
23885         
23886         // if current menu does not match..
23887         
23888         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
23889                 
23890             this.tb.el.hide();
23891             ///console.log("show: " + tn);
23892             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
23893             this.tb.el.show();
23894             // update name
23895             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
23896             
23897             
23898             // update attributes
23899             if (this.tb.fields) {
23900                 this.tb.fields.each(function(e) {
23901                     if (e.stylename) {
23902                         e.setValue(sel.style[e.stylename]);
23903                         return;
23904                     } 
23905                    e.setValue(sel.getAttribute(e.attrname));
23906                 });
23907             }
23908             
23909             var hasStyles = false;
23910             for(var i in this.styles) {
23911                 hasStyles = true;
23912                 break;
23913             }
23914             
23915             // update styles
23916             if (hasStyles) { 
23917                 var st = this.tb.fields.item(0);
23918                 
23919                 st.store.removeAll();
23920                
23921                 
23922                 var cn = sel.className.split(/\s+/);
23923                 
23924                 var avs = [];
23925                 if (this.styles['*']) {
23926                     
23927                     Roo.each(this.styles['*'], function(v) {
23928                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
23929                     });
23930                 }
23931                 if (this.styles[tn]) { 
23932                     Roo.each(this.styles[tn], function(v) {
23933                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
23934                     });
23935                 }
23936                 
23937                 st.store.loadData(avs);
23938                 st.collapse();
23939                 st.setValue(cn);
23940             }
23941             // flag our selected Node.
23942             this.tb.selectedNode = sel;
23943            
23944            
23945             Roo.menu.MenuMgr.hideAll();
23946
23947         }
23948         
23949         if (!updateFooter) {
23950             //this.footDisp.dom.innerHTML = ''; 
23951             return;
23952         }
23953         // update the footer
23954         //
23955         var html = '';
23956         
23957         this.footerEls = ans.reverse();
23958         Roo.each(this.footerEls, function(a,i) {
23959             if (!a) { return; }
23960             html += html.length ? ' &gt; '  :  '';
23961             
23962             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
23963             
23964         });
23965        
23966         // 
23967         var sz = this.footDisp.up('td').getSize();
23968         this.footDisp.dom.style.width = (sz.width -10) + 'px';
23969         this.footDisp.dom.style.marginLeft = '5px';
23970         
23971         this.footDisp.dom.style.overflow = 'hidden';
23972         
23973         this.footDisp.dom.innerHTML = html;
23974             
23975         //this.editorsyncValue();
23976     },
23977      
23978     
23979    
23980        
23981     // private
23982     onDestroy : function(){
23983         if(this.rendered){
23984             
23985             this.tb.items.each(function(item){
23986                 if(item.menu){
23987                     item.menu.removeAll();
23988                     if(item.menu.el){
23989                         item.menu.el.destroy();
23990                     }
23991                 }
23992                 item.destroy();
23993             });
23994              
23995         }
23996     },
23997     onFirstFocus: function() {
23998         // need to do this for all the toolbars..
23999         this.tb.items.each(function(item){
24000            item.enable();
24001         });
24002     },
24003     buildToolbar: function(tlist, nm)
24004     {
24005         var editor = this.editor;
24006         var editorcore = this.editorcore;
24007          // create a new element.
24008         var wdiv = editor.wrap.createChild({
24009                 tag: 'div'
24010             }, editor.wrap.dom.firstChild.nextSibling, true);
24011         
24012        
24013         var tb = new Roo.Toolbar(wdiv);
24014         // add the name..
24015         
24016         tb.add(nm+ ":&nbsp;");
24017         
24018         var styles = [];
24019         for(var i in this.styles) {
24020             styles.push(i);
24021         }
24022         
24023         // styles...
24024         if (styles && styles.length) {
24025             
24026             // this needs a multi-select checkbox...
24027             tb.addField( new Roo.form.ComboBox({
24028                 store: new Roo.data.SimpleStore({
24029                     id : 'val',
24030                     fields: ['val', 'selected'],
24031                     data : [] 
24032                 }),
24033                 name : '-roo-edit-className',
24034                 attrname : 'className',
24035                 displayField: 'val',
24036                 typeAhead: false,
24037                 mode: 'local',
24038                 editable : false,
24039                 triggerAction: 'all',
24040                 emptyText:'Select Style',
24041                 selectOnFocus:true,
24042                 width: 130,
24043                 listeners : {
24044                     'select': function(c, r, i) {
24045                         // initial support only for on class per el..
24046                         tb.selectedNode.className =  r ? r.get('val') : '';
24047                         editorcore.syncValue();
24048                     }
24049                 }
24050     
24051             }));
24052         }
24053         
24054         var tbc = Roo.form.HtmlEditor.ToolbarContext;
24055         var tbops = tbc.options;
24056         
24057         for (var i in tlist) {
24058             
24059             var item = tlist[i];
24060             tb.add(item.title + ":&nbsp;");
24061             
24062             
24063             //optname == used so you can configure the options available..
24064             var opts = item.opts ? item.opts : false;
24065             if (item.optname) {
24066                 opts = tbops[item.optname];
24067            
24068             }
24069             
24070             if (opts) {
24071                 // opts == pulldown..
24072                 tb.addField( new Roo.form.ComboBox({
24073                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
24074                         id : 'val',
24075                         fields: ['val', 'display'],
24076                         data : opts  
24077                     }),
24078                     name : '-roo-edit-' + i,
24079                     attrname : i,
24080                     stylename : item.style ? item.style : false,
24081                     displayField: item.displayField ? item.displayField : 'val',
24082                     valueField :  'val',
24083                     typeAhead: false,
24084                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
24085                     editable : false,
24086                     triggerAction: 'all',
24087                     emptyText:'Select',
24088                     selectOnFocus:true,
24089                     width: item.width ? item.width  : 130,
24090                     listeners : {
24091                         'select': function(c, r, i) {
24092                             if (c.stylename) {
24093                                 tb.selectedNode.style[c.stylename] =  r.get('val');
24094                                 return;
24095                             }
24096                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
24097                         }
24098                     }
24099
24100                 }));
24101                 continue;
24102                     
24103                  
24104                 
24105                 tb.addField( new Roo.form.TextField({
24106                     name: i,
24107                     width: 100,
24108                     //allowBlank:false,
24109                     value: ''
24110                 }));
24111                 continue;
24112             }
24113             tb.addField( new Roo.form.TextField({
24114                 name: '-roo-edit-' + i,
24115                 attrname : i,
24116                 
24117                 width: item.width,
24118                 //allowBlank:true,
24119                 value: '',
24120                 listeners: {
24121                     'change' : function(f, nv, ov) {
24122                         tb.selectedNode.setAttribute(f.attrname, nv);
24123                         editorcore.syncValue();
24124                     }
24125                 }
24126             }));
24127              
24128         }
24129         
24130         var _this = this;
24131         
24132         if(nm == 'BODY'){
24133             tb.addSeparator();
24134         
24135             tb.addButton( {
24136                 text: 'Stylesheets',
24137
24138                 listeners : {
24139                     click : function ()
24140                     {
24141                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
24142                     }
24143                 }
24144             });
24145         }
24146         
24147         tb.addFill();
24148         tb.addButton( {
24149             text: 'Remove Tag',
24150     
24151             listeners : {
24152                 click : function ()
24153                 {
24154                     // remove
24155                     // undo does not work.
24156                      
24157                     var sn = tb.selectedNode;
24158                     
24159                     var pn = sn.parentNode;
24160                     
24161                     var stn =  sn.childNodes[0];
24162                     var en = sn.childNodes[sn.childNodes.length - 1 ];
24163                     while (sn.childNodes.length) {
24164                         var node = sn.childNodes[0];
24165                         sn.removeChild(node);
24166                         //Roo.log(node);
24167                         pn.insertBefore(node, sn);
24168                         
24169                     }
24170                     pn.removeChild(sn);
24171                     var range = editorcore.createRange();
24172         
24173                     range.setStart(stn,0);
24174                     range.setEnd(en,0); //????
24175                     //range.selectNode(sel);
24176                     
24177                     
24178                     var selection = editorcore.getSelection();
24179                     selection.removeAllRanges();
24180                     selection.addRange(range);
24181                     
24182                     
24183                     
24184                     //_this.updateToolbar(null, null, pn);
24185                     _this.updateToolbar(null, null, null);
24186                     _this.footDisp.dom.innerHTML = ''; 
24187                 }
24188             }
24189             
24190                     
24191                 
24192             
24193         });
24194         
24195         
24196         tb.el.on('click', function(e){
24197             e.preventDefault(); // what does this do?
24198         });
24199         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
24200         tb.el.hide();
24201         tb.name = nm;
24202         // dont need to disable them... as they will get hidden
24203         return tb;
24204          
24205         
24206     },
24207     buildFooter : function()
24208     {
24209         
24210         var fel = this.editor.wrap.createChild();
24211         this.footer = new Roo.Toolbar(fel);
24212         // toolbar has scrolly on left / right?
24213         var footDisp= new Roo.Toolbar.Fill();
24214         var _t = this;
24215         this.footer.add(
24216             {
24217                 text : '&lt;',
24218                 xtype: 'Button',
24219                 handler : function() {
24220                     _t.footDisp.scrollTo('left',0,true)
24221                 }
24222             }
24223         );
24224         this.footer.add( footDisp );
24225         this.footer.add( 
24226             {
24227                 text : '&gt;',
24228                 xtype: 'Button',
24229                 handler : function() {
24230                     // no animation..
24231                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
24232                 }
24233             }
24234         );
24235         var fel = Roo.get(footDisp.el);
24236         fel.addClass('x-editor-context');
24237         this.footDispWrap = fel; 
24238         this.footDispWrap.overflow  = 'hidden';
24239         
24240         this.footDisp = fel.createChild();
24241         this.footDispWrap.on('click', this.onContextClick, this)
24242         
24243         
24244     },
24245     onContextClick : function (ev,dom)
24246     {
24247         ev.preventDefault();
24248         var  cn = dom.className;
24249         //Roo.log(cn);
24250         if (!cn.match(/x-ed-loc-/)) {
24251             return;
24252         }
24253         var n = cn.split('-').pop();
24254         var ans = this.footerEls;
24255         var sel = ans[n];
24256         
24257          // pick
24258         var range = this.editorcore.createRange();
24259         
24260         range.selectNodeContents(sel);
24261         //range.selectNode(sel);
24262         
24263         
24264         var selection = this.editorcore.getSelection();
24265         selection.removeAllRanges();
24266         selection.addRange(range);
24267         
24268         
24269         
24270         this.updateToolbar(null, null, sel);
24271         
24272         
24273     }
24274     
24275     
24276     
24277     
24278     
24279 });
24280
24281
24282
24283
24284
24285 /*
24286  * Based on:
24287  * Ext JS Library 1.1.1
24288  * Copyright(c) 2006-2007, Ext JS, LLC.
24289  *
24290  * Originally Released Under LGPL - original licence link has changed is not relivant.
24291  *
24292  * Fork - LGPL
24293  * <script type="text/javascript">
24294  */
24295  
24296 /**
24297  * @class Roo.form.BasicForm
24298  * @extends Roo.util.Observable
24299  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
24300  * @constructor
24301  * @param {String/HTMLElement/Roo.Element} el The form element or its id
24302  * @param {Object} config Configuration options
24303  */
24304 Roo.form.BasicForm = function(el, config){
24305     this.allItems = [];
24306     this.childForms = [];
24307     Roo.apply(this, config);
24308     /*
24309      * The Roo.form.Field items in this form.
24310      * @type MixedCollection
24311      */
24312      
24313      
24314     this.items = new Roo.util.MixedCollection(false, function(o){
24315         return o.id || (o.id = Roo.id());
24316     });
24317     this.addEvents({
24318         /**
24319          * @event beforeaction
24320          * Fires before any action is performed. Return false to cancel the action.
24321          * @param {Form} this
24322          * @param {Action} action The action to be performed
24323          */
24324         beforeaction: true,
24325         /**
24326          * @event actionfailed
24327          * Fires when an action fails.
24328          * @param {Form} this
24329          * @param {Action} action The action that failed
24330          */
24331         actionfailed : true,
24332         /**
24333          * @event actioncomplete
24334          * Fires when an action is completed.
24335          * @param {Form} this
24336          * @param {Action} action The action that completed
24337          */
24338         actioncomplete : true
24339     });
24340     if(el){
24341         this.initEl(el);
24342     }
24343     Roo.form.BasicForm.superclass.constructor.call(this);
24344 };
24345
24346 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
24347     /**
24348      * @cfg {String} method
24349      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
24350      */
24351     /**
24352      * @cfg {DataReader} reader
24353      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
24354      * This is optional as there is built-in support for processing JSON.
24355      */
24356     /**
24357      * @cfg {DataReader} errorReader
24358      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
24359      * This is completely optional as there is built-in support for processing JSON.
24360      */
24361     /**
24362      * @cfg {String} url
24363      * The URL to use for form actions if one isn't supplied in the action options.
24364      */
24365     /**
24366      * @cfg {Boolean} fileUpload
24367      * Set to true if this form is a file upload.
24368      */
24369      
24370     /**
24371      * @cfg {Object} baseParams
24372      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
24373      */
24374      /**
24375      
24376     /**
24377      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
24378      */
24379     timeout: 30,
24380
24381     // private
24382     activeAction : null,
24383
24384     /**
24385      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
24386      * or setValues() data instead of when the form was first created.
24387      */
24388     trackResetOnLoad : false,
24389     
24390     
24391     /**
24392      * childForms - used for multi-tab forms
24393      * @type {Array}
24394      */
24395     childForms : false,
24396     
24397     /**
24398      * allItems - full list of fields.
24399      * @type {Array}
24400      */
24401     allItems : false,
24402     
24403     /**
24404      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
24405      * element by passing it or its id or mask the form itself by passing in true.
24406      * @type Mixed
24407      */
24408     waitMsgTarget : false,
24409     
24410     /**
24411      * @type Boolean
24412      */
24413     disableMask : false,
24414
24415     // private
24416     initEl : function(el){
24417         this.el = Roo.get(el);
24418         this.id = this.el.id || Roo.id();
24419         this.el.on('submit', this.onSubmit, this);
24420         this.el.addClass('x-form');
24421     },
24422
24423     // private
24424     onSubmit : function(e){
24425         e.stopEvent();
24426     },
24427
24428     /**
24429      * Returns true if client-side validation on the form is successful.
24430      * @return Boolean
24431      */
24432     isValid : function(){
24433         var valid = true;
24434         this.items.each(function(f){
24435            if(!f.validate()){
24436                valid = false;
24437            }
24438         });
24439         return valid;
24440     },
24441
24442     /**
24443      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
24444      * @return Boolean
24445      */
24446     isDirty : function(){
24447         var dirty = false;
24448         this.items.each(function(f){
24449            if(f.isDirty()){
24450                dirty = true;
24451                return false;
24452            }
24453         });
24454         return dirty;
24455     },
24456     
24457     /**
24458      * Returns true if any fields in this form have changed since their original load. (New version)
24459      * @return Boolean
24460      */
24461     
24462     hasChanged : function()
24463     {
24464         var dirty = false;
24465         this.items.each(function(f){
24466            if(f.hasChanged()){
24467                dirty = true;
24468                return false;
24469            }
24470         });
24471         return dirty;
24472         
24473     },
24474     /**
24475      * Resets all hasChanged to 'false' -
24476      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
24477      * So hasChanged storage is only to be used for this purpose
24478      * @return Boolean
24479      */
24480     resetHasChanged : function()
24481     {
24482         this.items.each(function(f){
24483            f.resetHasChanged();
24484         });
24485         
24486     },
24487     
24488     
24489     /**
24490      * Performs a predefined action (submit or load) or custom actions you define on this form.
24491      * @param {String} actionName The name of the action type
24492      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
24493      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
24494      * accept other config options):
24495      * <pre>
24496 Property          Type             Description
24497 ----------------  ---------------  ----------------------------------------------------------------------------------
24498 url               String           The url for the action (defaults to the form's url)
24499 method            String           The form method to use (defaults to the form's method, or POST if not defined)
24500 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
24501 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
24502                                    validate the form on the client (defaults to false)
24503      * </pre>
24504      * @return {BasicForm} this
24505      */
24506     doAction : function(action, options){
24507         if(typeof action == 'string'){
24508             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
24509         }
24510         if(this.fireEvent('beforeaction', this, action) !== false){
24511             this.beforeAction(action);
24512             action.run.defer(100, action);
24513         }
24514         return this;
24515     },
24516
24517     /**
24518      * Shortcut to do a submit action.
24519      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
24520      * @return {BasicForm} this
24521      */
24522     submit : function(options){
24523         this.doAction('submit', options);
24524         return this;
24525     },
24526
24527     /**
24528      * Shortcut to do a load action.
24529      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
24530      * @return {BasicForm} this
24531      */
24532     load : function(options){
24533         this.doAction('load', options);
24534         return this;
24535     },
24536
24537     /**
24538      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
24539      * @param {Record} record The record to edit
24540      * @return {BasicForm} this
24541      */
24542     updateRecord : function(record){
24543         record.beginEdit();
24544         var fs = record.fields;
24545         fs.each(function(f){
24546             var field = this.findField(f.name);
24547             if(field){
24548                 record.set(f.name, field.getValue());
24549             }
24550         }, this);
24551         record.endEdit();
24552         return this;
24553     },
24554
24555     /**
24556      * Loads an Roo.data.Record into this form.
24557      * @param {Record} record The record to load
24558      * @return {BasicForm} this
24559      */
24560     loadRecord : function(record){
24561         this.setValues(record.data);
24562         return this;
24563     },
24564
24565     // private
24566     beforeAction : function(action){
24567         var o = action.options;
24568         
24569         if(!this.disableMask) {
24570             if(this.waitMsgTarget === true){
24571                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
24572             }else if(this.waitMsgTarget){
24573                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
24574                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
24575             }else {
24576                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
24577             }
24578         }
24579         
24580          
24581     },
24582
24583     // private
24584     afterAction : function(action, success){
24585         this.activeAction = null;
24586         var o = action.options;
24587         
24588         if(!this.disableMask) {
24589             if(this.waitMsgTarget === true){
24590                 this.el.unmask();
24591             }else if(this.waitMsgTarget){
24592                 this.waitMsgTarget.unmask();
24593             }else{
24594                 Roo.MessageBox.updateProgress(1);
24595                 Roo.MessageBox.hide();
24596             }
24597         }
24598         
24599         if(success){
24600             if(o.reset){
24601                 this.reset();
24602             }
24603             Roo.callback(o.success, o.scope, [this, action]);
24604             this.fireEvent('actioncomplete', this, action);
24605             
24606         }else{
24607             
24608             // failure condition..
24609             // we have a scenario where updates need confirming.
24610             // eg. if a locking scenario exists..
24611             // we look for { errors : { needs_confirm : true }} in the response.
24612             if (
24613                 (typeof(action.result) != 'undefined')  &&
24614                 (typeof(action.result.errors) != 'undefined')  &&
24615                 (typeof(action.result.errors.needs_confirm) != 'undefined')
24616            ){
24617                 var _t = this;
24618                 Roo.MessageBox.confirm(
24619                     "Change requires confirmation",
24620                     action.result.errorMsg,
24621                     function(r) {
24622                         if (r != 'yes') {
24623                             return;
24624                         }
24625                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
24626                     }
24627                     
24628                 );
24629                 
24630                 
24631                 
24632                 return;
24633             }
24634             
24635             Roo.callback(o.failure, o.scope, [this, action]);
24636             // show an error message if no failed handler is set..
24637             if (!this.hasListener('actionfailed')) {
24638                 Roo.MessageBox.alert("Error",
24639                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
24640                         action.result.errorMsg :
24641                         "Saving Failed, please check your entries or try again"
24642                 );
24643             }
24644             
24645             this.fireEvent('actionfailed', this, action);
24646         }
24647         
24648     },
24649
24650     /**
24651      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
24652      * @param {String} id The value to search for
24653      * @return Field
24654      */
24655     findField : function(id){
24656         var field = this.items.get(id);
24657         if(!field){
24658             this.items.each(function(f){
24659                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
24660                     field = f;
24661                     return false;
24662                 }
24663             });
24664         }
24665         return field || null;
24666     },
24667
24668     /**
24669      * Add a secondary form to this one, 
24670      * Used to provide tabbed forms. One form is primary, with hidden values 
24671      * which mirror the elements from the other forms.
24672      * 
24673      * @param {Roo.form.Form} form to add.
24674      * 
24675      */
24676     addForm : function(form)
24677     {
24678        
24679         if (this.childForms.indexOf(form) > -1) {
24680             // already added..
24681             return;
24682         }
24683         this.childForms.push(form);
24684         var n = '';
24685         Roo.each(form.allItems, function (fe) {
24686             
24687             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
24688             if (this.findField(n)) { // already added..
24689                 return;
24690             }
24691             var add = new Roo.form.Hidden({
24692                 name : n
24693             });
24694             add.render(this.el);
24695             
24696             this.add( add );
24697         }, this);
24698         
24699     },
24700     /**
24701      * Mark fields in this form invalid in bulk.
24702      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
24703      * @return {BasicForm} this
24704      */
24705     markInvalid : function(errors){
24706         if(errors instanceof Array){
24707             for(var i = 0, len = errors.length; i < len; i++){
24708                 var fieldError = errors[i];
24709                 var f = this.findField(fieldError.id);
24710                 if(f){
24711                     f.markInvalid(fieldError.msg);
24712                 }
24713             }
24714         }else{
24715             var field, id;
24716             for(id in errors){
24717                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
24718                     field.markInvalid(errors[id]);
24719                 }
24720             }
24721         }
24722         Roo.each(this.childForms || [], function (f) {
24723             f.markInvalid(errors);
24724         });
24725         
24726         return this;
24727     },
24728
24729     /**
24730      * Set values for fields in this form in bulk.
24731      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
24732      * @return {BasicForm} this
24733      */
24734     setValues : function(values){
24735         if(values instanceof Array){ // array of objects
24736             for(var i = 0, len = values.length; i < len; i++){
24737                 var v = values[i];
24738                 var f = this.findField(v.id);
24739                 if(f){
24740                     f.setValue(v.value);
24741                     if(this.trackResetOnLoad){
24742                         f.originalValue = f.getValue();
24743                     }
24744                 }
24745             }
24746         }else{ // object hash
24747             var field, id;
24748             for(id in values){
24749                 if(typeof values[id] != 'function' && (field = this.findField(id))){
24750                     
24751                     if (field.setFromData && 
24752                         field.valueField && 
24753                         field.displayField &&
24754                         // combos' with local stores can 
24755                         // be queried via setValue()
24756                         // to set their value..
24757                         (field.store && !field.store.isLocal)
24758                         ) {
24759                         // it's a combo
24760                         var sd = { };
24761                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
24762                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
24763                         field.setFromData(sd);
24764                         
24765                     } else {
24766                         field.setValue(values[id]);
24767                     }
24768                     
24769                     
24770                     if(this.trackResetOnLoad){
24771                         field.originalValue = field.getValue();
24772                     }
24773                 }
24774             }
24775         }
24776         this.resetHasChanged();
24777         
24778         
24779         Roo.each(this.childForms || [], function (f) {
24780             f.setValues(values);
24781             f.resetHasChanged();
24782         });
24783                 
24784         return this;
24785     },
24786
24787     /**
24788      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
24789      * they are returned as an array.
24790      * @param {Boolean} asString
24791      * @return {Object}
24792      */
24793     getValues : function(asString){
24794         if (this.childForms) {
24795             // copy values from the child forms
24796             Roo.each(this.childForms, function (f) {
24797                 this.setValues(f.getValues());
24798             }, this);
24799         }
24800         
24801         
24802         
24803         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
24804         if(asString === true){
24805             return fs;
24806         }
24807         return Roo.urlDecode(fs);
24808     },
24809     
24810     /**
24811      * Returns the fields in this form as an object with key/value pairs. 
24812      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
24813      * @return {Object}
24814      */
24815     getFieldValues : function(with_hidden)
24816     {
24817         if (this.childForms) {
24818             // copy values from the child forms
24819             // should this call getFieldValues - probably not as we do not currently copy
24820             // hidden fields when we generate..
24821             Roo.each(this.childForms, function (f) {
24822                 this.setValues(f.getValues());
24823             }, this);
24824         }
24825         
24826         var ret = {};
24827         this.items.each(function(f){
24828             if (!f.getName()) {
24829                 return;
24830             }
24831             var v = f.getValue();
24832             if (f.inputType =='radio') {
24833                 if (typeof(ret[f.getName()]) == 'undefined') {
24834                     ret[f.getName()] = ''; // empty..
24835                 }
24836                 
24837                 if (!f.el.dom.checked) {
24838                     return;
24839                     
24840                 }
24841                 v = f.el.dom.value;
24842                 
24843             }
24844             
24845             // not sure if this supported any more..
24846             if ((typeof(v) == 'object') && f.getRawValue) {
24847                 v = f.getRawValue() ; // dates..
24848             }
24849             // combo boxes where name != hiddenName...
24850             if (f.name != f.getName()) {
24851                 ret[f.name] = f.getRawValue();
24852             }
24853             ret[f.getName()] = v;
24854         });
24855         
24856         return ret;
24857     },
24858
24859     /**
24860      * Clears all invalid messages in this form.
24861      * @return {BasicForm} this
24862      */
24863     clearInvalid : function(){
24864         this.items.each(function(f){
24865            f.clearInvalid();
24866         });
24867         
24868         Roo.each(this.childForms || [], function (f) {
24869             f.clearInvalid();
24870         });
24871         
24872         
24873         return this;
24874     },
24875
24876     /**
24877      * Resets this form.
24878      * @return {BasicForm} this
24879      */
24880     reset : function(){
24881         this.items.each(function(f){
24882             f.reset();
24883         });
24884         
24885         Roo.each(this.childForms || [], function (f) {
24886             f.reset();
24887         });
24888         this.resetHasChanged();
24889         
24890         return this;
24891     },
24892
24893     /**
24894      * Add Roo.form components to this form.
24895      * @param {Field} field1
24896      * @param {Field} field2 (optional)
24897      * @param {Field} etc (optional)
24898      * @return {BasicForm} this
24899      */
24900     add : function(){
24901         this.items.addAll(Array.prototype.slice.call(arguments, 0));
24902         return this;
24903     },
24904
24905
24906     /**
24907      * Removes a field from the items collection (does NOT remove its markup).
24908      * @param {Field} field
24909      * @return {BasicForm} this
24910      */
24911     remove : function(field){
24912         this.items.remove(field);
24913         return this;
24914     },
24915
24916     /**
24917      * Looks at the fields in this form, checks them for an id attribute,
24918      * and calls applyTo on the existing dom element with that id.
24919      * @return {BasicForm} this
24920      */
24921     render : function(){
24922         this.items.each(function(f){
24923             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
24924                 f.applyTo(f.id);
24925             }
24926         });
24927         return this;
24928     },
24929
24930     /**
24931      * Calls {@link Ext#apply} for all fields in this form with the passed object.
24932      * @param {Object} values
24933      * @return {BasicForm} this
24934      */
24935     applyToFields : function(o){
24936         this.items.each(function(f){
24937            Roo.apply(f, o);
24938         });
24939         return this;
24940     },
24941
24942     /**
24943      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
24944      * @param {Object} values
24945      * @return {BasicForm} this
24946      */
24947     applyIfToFields : function(o){
24948         this.items.each(function(f){
24949            Roo.applyIf(f, o);
24950         });
24951         return this;
24952     }
24953 });
24954
24955 // back compat
24956 Roo.BasicForm = Roo.form.BasicForm;/*
24957  * Based on:
24958  * Ext JS Library 1.1.1
24959  * Copyright(c) 2006-2007, Ext JS, LLC.
24960  *
24961  * Originally Released Under LGPL - original licence link has changed is not relivant.
24962  *
24963  * Fork - LGPL
24964  * <script type="text/javascript">
24965  */
24966
24967 /**
24968  * @class Roo.form.Form
24969  * @extends Roo.form.BasicForm
24970  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
24971  * @constructor
24972  * @param {Object} config Configuration options
24973  */
24974 Roo.form.Form = function(config){
24975     var xitems =  [];
24976     if (config.items) {
24977         xitems = config.items;
24978         delete config.items;
24979     }
24980    
24981     
24982     Roo.form.Form.superclass.constructor.call(this, null, config);
24983     this.url = this.url || this.action;
24984     if(!this.root){
24985         this.root = new Roo.form.Layout(Roo.applyIf({
24986             id: Roo.id()
24987         }, config));
24988     }
24989     this.active = this.root;
24990     /**
24991      * Array of all the buttons that have been added to this form via {@link addButton}
24992      * @type Array
24993      */
24994     this.buttons = [];
24995     this.allItems = [];
24996     this.addEvents({
24997         /**
24998          * @event clientvalidation
24999          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
25000          * @param {Form} this
25001          * @param {Boolean} valid true if the form has passed client-side validation
25002          */
25003         clientvalidation: true,
25004         /**
25005          * @event rendered
25006          * Fires when the form is rendered
25007          * @param {Roo.form.Form} form
25008          */
25009         rendered : true
25010     });
25011     
25012     if (this.progressUrl) {
25013             // push a hidden field onto the list of fields..
25014             this.addxtype( {
25015                     xns: Roo.form, 
25016                     xtype : 'Hidden', 
25017                     name : 'UPLOAD_IDENTIFIER' 
25018             });
25019         }
25020         
25021     
25022     Roo.each(xitems, this.addxtype, this);
25023     
25024     
25025     
25026 };
25027
25028 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
25029     /**
25030      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
25031      */
25032     /**
25033      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
25034      */
25035     /**
25036      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
25037      */
25038     buttonAlign:'center',
25039
25040     /**
25041      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
25042      */
25043     minButtonWidth:75,
25044
25045     /**
25046      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
25047      * This property cascades to child containers if not set.
25048      */
25049     labelAlign:'left',
25050
25051     /**
25052      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
25053      * fires a looping event with that state. This is required to bind buttons to the valid
25054      * state using the config value formBind:true on the button.
25055      */
25056     monitorValid : false,
25057
25058     /**
25059      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
25060      */
25061     monitorPoll : 200,
25062     
25063     /**
25064      * @cfg {String} progressUrl - Url to return progress data 
25065      */
25066     
25067     progressUrl : false,
25068     /**
25069      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
25070      * sending a formdata with extra parameters - eg uploaded elements.
25071      */
25072     
25073     formData : false,
25074     
25075     /**
25076      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
25077      * fields are added and the column is closed. If no fields are passed the column remains open
25078      * until end() is called.
25079      * @param {Object} config The config to pass to the column
25080      * @param {Field} field1 (optional)
25081      * @param {Field} field2 (optional)
25082      * @param {Field} etc (optional)
25083      * @return Column The column container object
25084      */
25085     column : function(c){
25086         var col = new Roo.form.Column(c);
25087         this.start(col);
25088         if(arguments.length > 1){ // duplicate code required because of Opera
25089             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25090             this.end();
25091         }
25092         return col;
25093     },
25094
25095     /**
25096      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
25097      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
25098      * until end() is called.
25099      * @param {Object} config The config to pass to the fieldset
25100      * @param {Field} field1 (optional)
25101      * @param {Field} field2 (optional)
25102      * @param {Field} etc (optional)
25103      * @return FieldSet The fieldset container object
25104      */
25105     fieldset : function(c){
25106         var fs = new Roo.form.FieldSet(c);
25107         this.start(fs);
25108         if(arguments.length > 1){ // duplicate code required because of Opera
25109             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25110             this.end();
25111         }
25112         return fs;
25113     },
25114
25115     /**
25116      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
25117      * fields are added and the container is closed. If no fields are passed the container remains open
25118      * until end() is called.
25119      * @param {Object} config The config to pass to the Layout
25120      * @param {Field} field1 (optional)
25121      * @param {Field} field2 (optional)
25122      * @param {Field} etc (optional)
25123      * @return Layout The container object
25124      */
25125     container : function(c){
25126         var l = new Roo.form.Layout(c);
25127         this.start(l);
25128         if(arguments.length > 1){ // duplicate code required because of Opera
25129             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25130             this.end();
25131         }
25132         return l;
25133     },
25134
25135     /**
25136      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
25137      * @param {Object} container A Roo.form.Layout or subclass of Layout
25138      * @return {Form} this
25139      */
25140     start : function(c){
25141         // cascade label info
25142         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
25143         this.active.stack.push(c);
25144         c.ownerCt = this.active;
25145         this.active = c;
25146         return this;
25147     },
25148
25149     /**
25150      * Closes the current open container
25151      * @return {Form} this
25152      */
25153     end : function(){
25154         if(this.active == this.root){
25155             return this;
25156         }
25157         this.active = this.active.ownerCt;
25158         return this;
25159     },
25160
25161     /**
25162      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
25163      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
25164      * as the label of the field.
25165      * @param {Field} field1
25166      * @param {Field} field2 (optional)
25167      * @param {Field} etc. (optional)
25168      * @return {Form} this
25169      */
25170     add : function(){
25171         this.active.stack.push.apply(this.active.stack, arguments);
25172         this.allItems.push.apply(this.allItems,arguments);
25173         var r = [];
25174         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
25175             if(a[i].isFormField){
25176                 r.push(a[i]);
25177             }
25178         }
25179         if(r.length > 0){
25180             Roo.form.Form.superclass.add.apply(this, r);
25181         }
25182         return this;
25183     },
25184     
25185
25186     
25187     
25188     
25189      /**
25190      * Find any element that has been added to a form, using it's ID or name
25191      * This can include framesets, columns etc. along with regular fields..
25192      * @param {String} id - id or name to find.
25193      
25194      * @return {Element} e - or false if nothing found.
25195      */
25196     findbyId : function(id)
25197     {
25198         var ret = false;
25199         if (!id) {
25200             return ret;
25201         }
25202         Roo.each(this.allItems, function(f){
25203             if (f.id == id || f.name == id ){
25204                 ret = f;
25205                 return false;
25206             }
25207         });
25208         return ret;
25209     },
25210
25211     
25212     
25213     /**
25214      * Render this form into the passed container. This should only be called once!
25215      * @param {String/HTMLElement/Element} container The element this component should be rendered into
25216      * @return {Form} this
25217      */
25218     render : function(ct)
25219     {
25220         
25221         
25222         
25223         ct = Roo.get(ct);
25224         var o = this.autoCreate || {
25225             tag: 'form',
25226             method : this.method || 'POST',
25227             id : this.id || Roo.id()
25228         };
25229         this.initEl(ct.createChild(o));
25230
25231         this.root.render(this.el);
25232         
25233        
25234              
25235         this.items.each(function(f){
25236             f.render('x-form-el-'+f.id);
25237         });
25238
25239         if(this.buttons.length > 0){
25240             // tables are required to maintain order and for correct IE layout
25241             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
25242                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
25243                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
25244             }}, null, true);
25245             var tr = tb.getElementsByTagName('tr')[0];
25246             for(var i = 0, len = this.buttons.length; i < len; i++) {
25247                 var b = this.buttons[i];
25248                 var td = document.createElement('td');
25249                 td.className = 'x-form-btn-td';
25250                 b.render(tr.appendChild(td));
25251             }
25252         }
25253         if(this.monitorValid){ // initialize after render
25254             this.startMonitoring();
25255         }
25256         this.fireEvent('rendered', this);
25257         return this;
25258     },
25259
25260     /**
25261      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
25262      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
25263      * object or a valid Roo.DomHelper element config
25264      * @param {Function} handler The function called when the button is clicked
25265      * @param {Object} scope (optional) The scope of the handler function
25266      * @return {Roo.Button}
25267      */
25268     addButton : function(config, handler, scope){
25269         var bc = {
25270             handler: handler,
25271             scope: scope,
25272             minWidth: this.minButtonWidth,
25273             hideParent:true
25274         };
25275         if(typeof config == "string"){
25276             bc.text = config;
25277         }else{
25278             Roo.apply(bc, config);
25279         }
25280         var btn = new Roo.Button(null, bc);
25281         this.buttons.push(btn);
25282         return btn;
25283     },
25284
25285      /**
25286      * Adds a series of form elements (using the xtype property as the factory method.
25287      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
25288      * @param {Object} config 
25289      */
25290     
25291     addxtype : function()
25292     {
25293         var ar = Array.prototype.slice.call(arguments, 0);
25294         var ret = false;
25295         for(var i = 0; i < ar.length; i++) {
25296             if (!ar[i]) {
25297                 continue; // skip -- if this happends something invalid got sent, we 
25298                 // should ignore it, as basically that interface element will not show up
25299                 // and that should be pretty obvious!!
25300             }
25301             
25302             if (Roo.form[ar[i].xtype]) {
25303                 ar[i].form = this;
25304                 var fe = Roo.factory(ar[i], Roo.form);
25305                 if (!ret) {
25306                     ret = fe;
25307                 }
25308                 fe.form = this;
25309                 if (fe.store) {
25310                     fe.store.form = this;
25311                 }
25312                 if (fe.isLayout) {  
25313                          
25314                     this.start(fe);
25315                     this.allItems.push(fe);
25316                     if (fe.items && fe.addxtype) {
25317                         fe.addxtype.apply(fe, fe.items);
25318                         delete fe.items;
25319                     }
25320                      this.end();
25321                     continue;
25322                 }
25323                 
25324                 
25325                  
25326                 this.add(fe);
25327               //  console.log('adding ' + ar[i].xtype);
25328             }
25329             if (ar[i].xtype == 'Button') {  
25330                 //console.log('adding button');
25331                 //console.log(ar[i]);
25332                 this.addButton(ar[i]);
25333                 this.allItems.push(fe);
25334                 continue;
25335             }
25336             
25337             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
25338                 alert('end is not supported on xtype any more, use items');
25339             //    this.end();
25340             //    //console.log('adding end');
25341             }
25342             
25343         }
25344         return ret;
25345     },
25346     
25347     /**
25348      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
25349      * option "monitorValid"
25350      */
25351     startMonitoring : function(){
25352         if(!this.bound){
25353             this.bound = true;
25354             Roo.TaskMgr.start({
25355                 run : this.bindHandler,
25356                 interval : this.monitorPoll || 200,
25357                 scope: this
25358             });
25359         }
25360     },
25361
25362     /**
25363      * Stops monitoring of the valid state of this form
25364      */
25365     stopMonitoring : function(){
25366         this.bound = false;
25367     },
25368
25369     // private
25370     bindHandler : function(){
25371         if(!this.bound){
25372             return false; // stops binding
25373         }
25374         var valid = true;
25375         this.items.each(function(f){
25376             if(!f.isValid(true)){
25377                 valid = false;
25378                 return false;
25379             }
25380         });
25381         for(var i = 0, len = this.buttons.length; i < len; i++){
25382             var btn = this.buttons[i];
25383             if(btn.formBind === true && btn.disabled === valid){
25384                 btn.setDisabled(!valid);
25385             }
25386         }
25387         this.fireEvent('clientvalidation', this, valid);
25388     }
25389     
25390     
25391     
25392     
25393     
25394     
25395     
25396     
25397 });
25398
25399
25400 // back compat
25401 Roo.Form = Roo.form.Form;
25402 /*
25403  * Based on:
25404  * Ext JS Library 1.1.1
25405  * Copyright(c) 2006-2007, Ext JS, LLC.
25406  *
25407  * Originally Released Under LGPL - original licence link has changed is not relivant.
25408  *
25409  * Fork - LGPL
25410  * <script type="text/javascript">
25411  */
25412
25413 // as we use this in bootstrap.
25414 Roo.namespace('Roo.form');
25415  /**
25416  * @class Roo.form.Action
25417  * Internal Class used to handle form actions
25418  * @constructor
25419  * @param {Roo.form.BasicForm} el The form element or its id
25420  * @param {Object} config Configuration options
25421  */
25422
25423  
25424  
25425 // define the action interface
25426 Roo.form.Action = function(form, options){
25427     this.form = form;
25428     this.options = options || {};
25429 };
25430 /**
25431  * Client Validation Failed
25432  * @const 
25433  */
25434 Roo.form.Action.CLIENT_INVALID = 'client';
25435 /**
25436  * Server Validation Failed
25437  * @const 
25438  */
25439 Roo.form.Action.SERVER_INVALID = 'server';
25440  /**
25441  * Connect to Server Failed
25442  * @const 
25443  */
25444 Roo.form.Action.CONNECT_FAILURE = 'connect';
25445 /**
25446  * Reading Data from Server Failed
25447  * @const 
25448  */
25449 Roo.form.Action.LOAD_FAILURE = 'load';
25450
25451 Roo.form.Action.prototype = {
25452     type : 'default',
25453     failureType : undefined,
25454     response : undefined,
25455     result : undefined,
25456
25457     // interface method
25458     run : function(options){
25459
25460     },
25461
25462     // interface method
25463     success : function(response){
25464
25465     },
25466
25467     // interface method
25468     handleResponse : function(response){
25469
25470     },
25471
25472     // default connection failure
25473     failure : function(response){
25474         
25475         this.response = response;
25476         this.failureType = Roo.form.Action.CONNECT_FAILURE;
25477         this.form.afterAction(this, false);
25478     },
25479
25480     processResponse : function(response){
25481         this.response = response;
25482         if(!response.responseText){
25483             return true;
25484         }
25485         this.result = this.handleResponse(response);
25486         return this.result;
25487     },
25488
25489     // utility functions used internally
25490     getUrl : function(appendParams){
25491         var url = this.options.url || this.form.url || this.form.el.dom.action;
25492         if(appendParams){
25493             var p = this.getParams();
25494             if(p){
25495                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
25496             }
25497         }
25498         return url;
25499     },
25500
25501     getMethod : function(){
25502         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
25503     },
25504
25505     getParams : function(){
25506         var bp = this.form.baseParams;
25507         var p = this.options.params;
25508         if(p){
25509             if(typeof p == "object"){
25510                 p = Roo.urlEncode(Roo.applyIf(p, bp));
25511             }else if(typeof p == 'string' && bp){
25512                 p += '&' + Roo.urlEncode(bp);
25513             }
25514         }else if(bp){
25515             p = Roo.urlEncode(bp);
25516         }
25517         return p;
25518     },
25519
25520     createCallback : function(){
25521         return {
25522             success: this.success,
25523             failure: this.failure,
25524             scope: this,
25525             timeout: (this.form.timeout*1000),
25526             upload: this.form.fileUpload ? this.success : undefined
25527         };
25528     }
25529 };
25530
25531 Roo.form.Action.Submit = function(form, options){
25532     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
25533 };
25534
25535 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
25536     type : 'submit',
25537
25538     haveProgress : false,
25539     uploadComplete : false,
25540     
25541     // uploadProgress indicator.
25542     uploadProgress : function()
25543     {
25544         if (!this.form.progressUrl) {
25545             return;
25546         }
25547         
25548         if (!this.haveProgress) {
25549             Roo.MessageBox.progress("Uploading", "Uploading");
25550         }
25551         if (this.uploadComplete) {
25552            Roo.MessageBox.hide();
25553            return;
25554         }
25555         
25556         this.haveProgress = true;
25557    
25558         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
25559         
25560         var c = new Roo.data.Connection();
25561         c.request({
25562             url : this.form.progressUrl,
25563             params: {
25564                 id : uid
25565             },
25566             method: 'GET',
25567             success : function(req){
25568                //console.log(data);
25569                 var rdata = false;
25570                 var edata;
25571                 try  {
25572                    rdata = Roo.decode(req.responseText)
25573                 } catch (e) {
25574                     Roo.log("Invalid data from server..");
25575                     Roo.log(edata);
25576                     return;
25577                 }
25578                 if (!rdata || !rdata.success) {
25579                     Roo.log(rdata);
25580                     Roo.MessageBox.alert(Roo.encode(rdata));
25581                     return;
25582                 }
25583                 var data = rdata.data;
25584                 
25585                 if (this.uploadComplete) {
25586                    Roo.MessageBox.hide();
25587                    return;
25588                 }
25589                    
25590                 if (data){
25591                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
25592                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
25593                     );
25594                 }
25595                 this.uploadProgress.defer(2000,this);
25596             },
25597        
25598             failure: function(data) {
25599                 Roo.log('progress url failed ');
25600                 Roo.log(data);
25601             },
25602             scope : this
25603         });
25604            
25605     },
25606     
25607     
25608     run : function()
25609     {
25610         // run get Values on the form, so it syncs any secondary forms.
25611         this.form.getValues();
25612         
25613         var o = this.options;
25614         var method = this.getMethod();
25615         var isPost = method == 'POST';
25616         if(o.clientValidation === false || this.form.isValid()){
25617             
25618             if (this.form.progressUrl) {
25619                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
25620                     (new Date() * 1) + '' + Math.random());
25621                     
25622             } 
25623             
25624             
25625             Roo.Ajax.request(Roo.apply(this.createCallback(), {
25626                 form:this.form.el.dom,
25627                 url:this.getUrl(!isPost),
25628                 method: method,
25629                 params:isPost ? this.getParams() : null,
25630                 isUpload: this.form.fileUpload,
25631                 formData : this.form.formData
25632             }));
25633             
25634             this.uploadProgress();
25635
25636         }else if (o.clientValidation !== false){ // client validation failed
25637             this.failureType = Roo.form.Action.CLIENT_INVALID;
25638             this.form.afterAction(this, false);
25639         }
25640     },
25641
25642     success : function(response)
25643     {
25644         this.uploadComplete= true;
25645         if (this.haveProgress) {
25646             Roo.MessageBox.hide();
25647         }
25648         
25649         
25650         var result = this.processResponse(response);
25651         if(result === true || result.success){
25652             this.form.afterAction(this, true);
25653             return;
25654         }
25655         if(result.errors){
25656             this.form.markInvalid(result.errors);
25657             this.failureType = Roo.form.Action.SERVER_INVALID;
25658         }
25659         this.form.afterAction(this, false);
25660     },
25661     failure : function(response)
25662     {
25663         this.uploadComplete= true;
25664         if (this.haveProgress) {
25665             Roo.MessageBox.hide();
25666         }
25667         
25668         this.response = response;
25669         this.failureType = Roo.form.Action.CONNECT_FAILURE;
25670         this.form.afterAction(this, false);
25671     },
25672     
25673     handleResponse : function(response){
25674         if(this.form.errorReader){
25675             var rs = this.form.errorReader.read(response);
25676             var errors = [];
25677             if(rs.records){
25678                 for(var i = 0, len = rs.records.length; i < len; i++) {
25679                     var r = rs.records[i];
25680                     errors[i] = r.data;
25681                 }
25682             }
25683             if(errors.length < 1){
25684                 errors = null;
25685             }
25686             return {
25687                 success : rs.success,
25688                 errors : errors
25689             };
25690         }
25691         var ret = false;
25692         try {
25693             ret = Roo.decode(response.responseText);
25694         } catch (e) {
25695             ret = {
25696                 success: false,
25697                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
25698                 errors : []
25699             };
25700         }
25701         return ret;
25702         
25703     }
25704 });
25705
25706
25707 Roo.form.Action.Load = function(form, options){
25708     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
25709     this.reader = this.form.reader;
25710 };
25711
25712 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
25713     type : 'load',
25714
25715     run : function(){
25716         
25717         Roo.Ajax.request(Roo.apply(
25718                 this.createCallback(), {
25719                     method:this.getMethod(),
25720                     url:this.getUrl(false),
25721                     params:this.getParams()
25722         }));
25723     },
25724
25725     success : function(response){
25726         
25727         var result = this.processResponse(response);
25728         if(result === true || !result.success || !result.data){
25729             this.failureType = Roo.form.Action.LOAD_FAILURE;
25730             this.form.afterAction(this, false);
25731             return;
25732         }
25733         this.form.clearInvalid();
25734         this.form.setValues(result.data);
25735         this.form.afterAction(this, true);
25736     },
25737
25738     handleResponse : function(response){
25739         if(this.form.reader){
25740             var rs = this.form.reader.read(response);
25741             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
25742             return {
25743                 success : rs.success,
25744                 data : data
25745             };
25746         }
25747         return Roo.decode(response.responseText);
25748     }
25749 });
25750
25751 Roo.form.Action.ACTION_TYPES = {
25752     'load' : Roo.form.Action.Load,
25753     'submit' : Roo.form.Action.Submit
25754 };/*
25755  * Based on:
25756  * Ext JS Library 1.1.1
25757  * Copyright(c) 2006-2007, Ext JS, LLC.
25758  *
25759  * Originally Released Under LGPL - original licence link has changed is not relivant.
25760  *
25761  * Fork - LGPL
25762  * <script type="text/javascript">
25763  */
25764  
25765 /**
25766  * @class Roo.form.Layout
25767  * @extends Roo.Component
25768  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
25769  * @constructor
25770  * @param {Object} config Configuration options
25771  */
25772 Roo.form.Layout = function(config){
25773     var xitems = [];
25774     if (config.items) {
25775         xitems = config.items;
25776         delete config.items;
25777     }
25778     Roo.form.Layout.superclass.constructor.call(this, config);
25779     this.stack = [];
25780     Roo.each(xitems, this.addxtype, this);
25781      
25782 };
25783
25784 Roo.extend(Roo.form.Layout, Roo.Component, {
25785     /**
25786      * @cfg {String/Object} autoCreate
25787      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
25788      */
25789     /**
25790      * @cfg {String/Object/Function} style
25791      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
25792      * a function which returns such a specification.
25793      */
25794     /**
25795      * @cfg {String} labelAlign
25796      * Valid values are "left," "top" and "right" (defaults to "left")
25797      */
25798     /**
25799      * @cfg {Number} labelWidth
25800      * Fixed width in pixels of all field labels (defaults to undefined)
25801      */
25802     /**
25803      * @cfg {Boolean} clear
25804      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
25805      */
25806     clear : true,
25807     /**
25808      * @cfg {String} labelSeparator
25809      * The separator to use after field labels (defaults to ':')
25810      */
25811     labelSeparator : ':',
25812     /**
25813      * @cfg {Boolean} hideLabels
25814      * True to suppress the display of field labels in this layout (defaults to false)
25815      */
25816     hideLabels : false,
25817
25818     // private
25819     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
25820     
25821     isLayout : true,
25822     
25823     // private
25824     onRender : function(ct, position){
25825         if(this.el){ // from markup
25826             this.el = Roo.get(this.el);
25827         }else {  // generate
25828             var cfg = this.getAutoCreate();
25829             this.el = ct.createChild(cfg, position);
25830         }
25831         if(this.style){
25832             this.el.applyStyles(this.style);
25833         }
25834         if(this.labelAlign){
25835             this.el.addClass('x-form-label-'+this.labelAlign);
25836         }
25837         if(this.hideLabels){
25838             this.labelStyle = "display:none";
25839             this.elementStyle = "padding-left:0;";
25840         }else{
25841             if(typeof this.labelWidth == 'number'){
25842                 this.labelStyle = "width:"+this.labelWidth+"px;";
25843                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
25844             }
25845             if(this.labelAlign == 'top'){
25846                 this.labelStyle = "width:auto;";
25847                 this.elementStyle = "padding-left:0;";
25848             }
25849         }
25850         var stack = this.stack;
25851         var slen = stack.length;
25852         if(slen > 0){
25853             if(!this.fieldTpl){
25854                 var t = new Roo.Template(
25855                     '<div class="x-form-item {5}">',
25856                         '<label for="{0}" style="{2}">{1}{4}</label>',
25857                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
25858                         '</div>',
25859                     '</div><div class="x-form-clear-left"></div>'
25860                 );
25861                 t.disableFormats = true;
25862                 t.compile();
25863                 Roo.form.Layout.prototype.fieldTpl = t;
25864             }
25865             for(var i = 0; i < slen; i++) {
25866                 if(stack[i].isFormField){
25867                     this.renderField(stack[i]);
25868                 }else{
25869                     this.renderComponent(stack[i]);
25870                 }
25871             }
25872         }
25873         if(this.clear){
25874             this.el.createChild({cls:'x-form-clear'});
25875         }
25876     },
25877
25878     // private
25879     renderField : function(f){
25880         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
25881                f.id, //0
25882                f.fieldLabel, //1
25883                f.labelStyle||this.labelStyle||'', //2
25884                this.elementStyle||'', //3
25885                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
25886                f.itemCls||this.itemCls||''  //5
25887        ], true).getPrevSibling());
25888     },
25889
25890     // private
25891     renderComponent : function(c){
25892         c.render(c.isLayout ? this.el : this.el.createChild());    
25893     },
25894     /**
25895      * Adds a object form elements (using the xtype property as the factory method.)
25896      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
25897      * @param {Object} config 
25898      */
25899     addxtype : function(o)
25900     {
25901         // create the lement.
25902         o.form = this.form;
25903         var fe = Roo.factory(o, Roo.form);
25904         this.form.allItems.push(fe);
25905         this.stack.push(fe);
25906         
25907         if (fe.isFormField) {
25908             this.form.items.add(fe);
25909         }
25910          
25911         return fe;
25912     }
25913 });
25914
25915 /**
25916  * @class Roo.form.Column
25917  * @extends Roo.form.Layout
25918  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
25919  * @constructor
25920  * @param {Object} config Configuration options
25921  */
25922 Roo.form.Column = function(config){
25923     Roo.form.Column.superclass.constructor.call(this, config);
25924 };
25925
25926 Roo.extend(Roo.form.Column, Roo.form.Layout, {
25927     /**
25928      * @cfg {Number/String} width
25929      * The fixed width of the column in pixels or CSS value (defaults to "auto")
25930      */
25931     /**
25932      * @cfg {String/Object} autoCreate
25933      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
25934      */
25935
25936     // private
25937     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
25938
25939     // private
25940     onRender : function(ct, position){
25941         Roo.form.Column.superclass.onRender.call(this, ct, position);
25942         if(this.width){
25943             this.el.setWidth(this.width);
25944         }
25945     }
25946 });
25947
25948
25949 /**
25950  * @class Roo.form.Row
25951  * @extends Roo.form.Layout
25952  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
25953  * @constructor
25954  * @param {Object} config Configuration options
25955  */
25956
25957  
25958 Roo.form.Row = function(config){
25959     Roo.form.Row.superclass.constructor.call(this, config);
25960 };
25961  
25962 Roo.extend(Roo.form.Row, Roo.form.Layout, {
25963       /**
25964      * @cfg {Number/String} width
25965      * The fixed width of the column in pixels or CSS value (defaults to "auto")
25966      */
25967     /**
25968      * @cfg {Number/String} height
25969      * The fixed height of the column in pixels or CSS value (defaults to "auto")
25970      */
25971     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
25972     
25973     padWidth : 20,
25974     // private
25975     onRender : function(ct, position){
25976         //console.log('row render');
25977         if(!this.rowTpl){
25978             var t = new Roo.Template(
25979                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
25980                     '<label for="{0}" style="{2}">{1}{4}</label>',
25981                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
25982                     '</div>',
25983                 '</div>'
25984             );
25985             t.disableFormats = true;
25986             t.compile();
25987             Roo.form.Layout.prototype.rowTpl = t;
25988         }
25989         this.fieldTpl = this.rowTpl;
25990         
25991         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
25992         var labelWidth = 100;
25993         
25994         if ((this.labelAlign != 'top')) {
25995             if (typeof this.labelWidth == 'number') {
25996                 labelWidth = this.labelWidth
25997             }
25998             this.padWidth =  20 + labelWidth;
25999             
26000         }
26001         
26002         Roo.form.Column.superclass.onRender.call(this, ct, position);
26003         if(this.width){
26004             this.el.setWidth(this.width);
26005         }
26006         if(this.height){
26007             this.el.setHeight(this.height);
26008         }
26009     },
26010     
26011     // private
26012     renderField : function(f){
26013         f.fieldEl = this.fieldTpl.append(this.el, [
26014                f.id, f.fieldLabel,
26015                f.labelStyle||this.labelStyle||'',
26016                this.elementStyle||'',
26017                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
26018                f.itemCls||this.itemCls||'',
26019                f.width ? f.width + this.padWidth : 160 + this.padWidth
26020        ],true);
26021     }
26022 });
26023  
26024
26025 /**
26026  * @class Roo.form.FieldSet
26027  * @extends Roo.form.Layout
26028  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
26029  * @constructor
26030  * @param {Object} config Configuration options
26031  */
26032 Roo.form.FieldSet = function(config){
26033     Roo.form.FieldSet.superclass.constructor.call(this, config);
26034 };
26035
26036 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
26037     /**
26038      * @cfg {String} legend
26039      * The text to display as the legend for the FieldSet (defaults to '')
26040      */
26041     /**
26042      * @cfg {String/Object} autoCreate
26043      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
26044      */
26045
26046     // private
26047     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
26048
26049     // private
26050     onRender : function(ct, position){
26051         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
26052         if(this.legend){
26053             this.setLegend(this.legend);
26054         }
26055     },
26056
26057     // private
26058     setLegend : function(text){
26059         if(this.rendered){
26060             this.el.child('legend').update(text);
26061         }
26062     }
26063 });/*
26064  * Based on:
26065  * Ext JS Library 1.1.1
26066  * Copyright(c) 2006-2007, Ext JS, LLC.
26067  *
26068  * Originally Released Under LGPL - original licence link has changed is not relivant.
26069  *
26070  * Fork - LGPL
26071  * <script type="text/javascript">
26072  */
26073 /**
26074  * @class Roo.form.VTypes
26075  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
26076  * @singleton
26077  */
26078 Roo.form.VTypes = function(){
26079     // closure these in so they are only created once.
26080     var alpha = /^[a-zA-Z_]+$/;
26081     var alphanum = /^[a-zA-Z0-9_]+$/;
26082     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
26083     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
26084
26085     // All these messages and functions are configurable
26086     return {
26087         /**
26088          * The function used to validate email addresses
26089          * @param {String} value The email address
26090          */
26091         'email' : function(v){
26092             return email.test(v);
26093         },
26094         /**
26095          * The error text to display when the email validation function returns false
26096          * @type String
26097          */
26098         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
26099         /**
26100          * The keystroke filter mask to be applied on email input
26101          * @type RegExp
26102          */
26103         'emailMask' : /[a-z0-9_\.\-@]/i,
26104
26105         /**
26106          * The function used to validate URLs
26107          * @param {String} value The URL
26108          */
26109         'url' : function(v){
26110             return url.test(v);
26111         },
26112         /**
26113          * The error text to display when the url validation function returns false
26114          * @type String
26115          */
26116         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
26117         
26118         /**
26119          * The function used to validate alpha values
26120          * @param {String} value The value
26121          */
26122         'alpha' : function(v){
26123             return alpha.test(v);
26124         },
26125         /**
26126          * The error text to display when the alpha validation function returns false
26127          * @type String
26128          */
26129         'alphaText' : 'This field should only contain letters and _',
26130         /**
26131          * The keystroke filter mask to be applied on alpha input
26132          * @type RegExp
26133          */
26134         'alphaMask' : /[a-z_]/i,
26135
26136         /**
26137          * The function used to validate alphanumeric values
26138          * @param {String} value The value
26139          */
26140         'alphanum' : function(v){
26141             return alphanum.test(v);
26142         },
26143         /**
26144          * The error text to display when the alphanumeric validation function returns false
26145          * @type String
26146          */
26147         'alphanumText' : 'This field should only contain letters, numbers and _',
26148         /**
26149          * The keystroke filter mask to be applied on alphanumeric input
26150          * @type RegExp
26151          */
26152         'alphanumMask' : /[a-z0-9_]/i
26153     };
26154 }();//<script type="text/javascript">
26155
26156 /**
26157  * @class Roo.form.FCKeditor
26158  * @extends Roo.form.TextArea
26159  * Wrapper around the FCKEditor http://www.fckeditor.net
26160  * @constructor
26161  * Creates a new FCKeditor
26162  * @param {Object} config Configuration options
26163  */
26164 Roo.form.FCKeditor = function(config){
26165     Roo.form.FCKeditor.superclass.constructor.call(this, config);
26166     this.addEvents({
26167          /**
26168          * @event editorinit
26169          * Fired when the editor is initialized - you can add extra handlers here..
26170          * @param {FCKeditor} this
26171          * @param {Object} the FCK object.
26172          */
26173         editorinit : true
26174     });
26175     
26176     
26177 };
26178 Roo.form.FCKeditor.editors = { };
26179 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
26180 {
26181     //defaultAutoCreate : {
26182     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
26183     //},
26184     // private
26185     /**
26186      * @cfg {Object} fck options - see fck manual for details.
26187      */
26188     fckconfig : false,
26189     
26190     /**
26191      * @cfg {Object} fck toolbar set (Basic or Default)
26192      */
26193     toolbarSet : 'Basic',
26194     /**
26195      * @cfg {Object} fck BasePath
26196      */ 
26197     basePath : '/fckeditor/',
26198     
26199     
26200     frame : false,
26201     
26202     value : '',
26203     
26204    
26205     onRender : function(ct, position)
26206     {
26207         if(!this.el){
26208             this.defaultAutoCreate = {
26209                 tag: "textarea",
26210                 style:"width:300px;height:60px;",
26211                 autocomplete: "new-password"
26212             };
26213         }
26214         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
26215         /*
26216         if(this.grow){
26217             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
26218             if(this.preventScrollbars){
26219                 this.el.setStyle("overflow", "hidden");
26220             }
26221             this.el.setHeight(this.growMin);
26222         }
26223         */
26224         //console.log('onrender' + this.getId() );
26225         Roo.form.FCKeditor.editors[this.getId()] = this;
26226          
26227
26228         this.replaceTextarea() ;
26229         
26230     },
26231     
26232     getEditor : function() {
26233         return this.fckEditor;
26234     },
26235     /**
26236      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
26237      * @param {Mixed} value The value to set
26238      */
26239     
26240     
26241     setValue : function(value)
26242     {
26243         //console.log('setValue: ' + value);
26244         
26245         if(typeof(value) == 'undefined') { // not sure why this is happending...
26246             return;
26247         }
26248         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26249         
26250         //if(!this.el || !this.getEditor()) {
26251         //    this.value = value;
26252             //this.setValue.defer(100,this,[value]);    
26253         //    return;
26254         //} 
26255         
26256         if(!this.getEditor()) {
26257             return;
26258         }
26259         
26260         this.getEditor().SetData(value);
26261         
26262         //
26263
26264     },
26265
26266     /**
26267      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
26268      * @return {Mixed} value The field value
26269      */
26270     getValue : function()
26271     {
26272         
26273         if (this.frame && this.frame.dom.style.display == 'none') {
26274             return Roo.form.FCKeditor.superclass.getValue.call(this);
26275         }
26276         
26277         if(!this.el || !this.getEditor()) {
26278            
26279            // this.getValue.defer(100,this); 
26280             return this.value;
26281         }
26282        
26283         
26284         var value=this.getEditor().GetData();
26285         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26286         return Roo.form.FCKeditor.superclass.getValue.call(this);
26287         
26288
26289     },
26290
26291     /**
26292      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
26293      * @return {Mixed} value The field value
26294      */
26295     getRawValue : function()
26296     {
26297         if (this.frame && this.frame.dom.style.display == 'none') {
26298             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26299         }
26300         
26301         if(!this.el || !this.getEditor()) {
26302             //this.getRawValue.defer(100,this); 
26303             return this.value;
26304             return;
26305         }
26306         
26307         
26308         
26309         var value=this.getEditor().GetData();
26310         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
26311         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26312          
26313     },
26314     
26315     setSize : function(w,h) {
26316         
26317         
26318         
26319         //if (this.frame && this.frame.dom.style.display == 'none') {
26320         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26321         //    return;
26322         //}
26323         //if(!this.el || !this.getEditor()) {
26324         //    this.setSize.defer(100,this, [w,h]); 
26325         //    return;
26326         //}
26327         
26328         
26329         
26330         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26331         
26332         this.frame.dom.setAttribute('width', w);
26333         this.frame.dom.setAttribute('height', h);
26334         this.frame.setSize(w,h);
26335         
26336     },
26337     
26338     toggleSourceEdit : function(value) {
26339         
26340       
26341          
26342         this.el.dom.style.display = value ? '' : 'none';
26343         this.frame.dom.style.display = value ?  'none' : '';
26344         
26345     },
26346     
26347     
26348     focus: function(tag)
26349     {
26350         if (this.frame.dom.style.display == 'none') {
26351             return Roo.form.FCKeditor.superclass.focus.call(this);
26352         }
26353         if(!this.el || !this.getEditor()) {
26354             this.focus.defer(100,this, [tag]); 
26355             return;
26356         }
26357         
26358         
26359         
26360         
26361         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
26362         this.getEditor().Focus();
26363         if (tgs.length) {
26364             if (!this.getEditor().Selection.GetSelection()) {
26365                 this.focus.defer(100,this, [tag]); 
26366                 return;
26367             }
26368             
26369             
26370             var r = this.getEditor().EditorDocument.createRange();
26371             r.setStart(tgs[0],0);
26372             r.setEnd(tgs[0],0);
26373             this.getEditor().Selection.GetSelection().removeAllRanges();
26374             this.getEditor().Selection.GetSelection().addRange(r);
26375             this.getEditor().Focus();
26376         }
26377         
26378     },
26379     
26380     
26381     
26382     replaceTextarea : function()
26383     {
26384         if ( document.getElementById( this.getId() + '___Frame' ) ) {
26385             return ;
26386         }
26387         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
26388         //{
26389             // We must check the elements firstly using the Id and then the name.
26390         var oTextarea = document.getElementById( this.getId() );
26391         
26392         var colElementsByName = document.getElementsByName( this.getId() ) ;
26393          
26394         oTextarea.style.display = 'none' ;
26395
26396         if ( oTextarea.tabIndex ) {            
26397             this.TabIndex = oTextarea.tabIndex ;
26398         }
26399         
26400         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
26401         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
26402         this.frame = Roo.get(this.getId() + '___Frame')
26403     },
26404     
26405     _getConfigHtml : function()
26406     {
26407         var sConfig = '' ;
26408
26409         for ( var o in this.fckconfig ) {
26410             sConfig += sConfig.length > 0  ? '&amp;' : '';
26411             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
26412         }
26413
26414         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
26415     },
26416     
26417     
26418     _getIFrameHtml : function()
26419     {
26420         var sFile = 'fckeditor.html' ;
26421         /* no idea what this is about..
26422         try
26423         {
26424             if ( (/fcksource=true/i).test( window.top.location.search ) )
26425                 sFile = 'fckeditor.original.html' ;
26426         }
26427         catch (e) { 
26428         */
26429
26430         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
26431         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
26432         
26433         
26434         var html = '<iframe id="' + this.getId() +
26435             '___Frame" src="' + sLink +
26436             '" width="' + this.width +
26437             '" height="' + this.height + '"' +
26438             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
26439             ' frameborder="0" scrolling="no"></iframe>' ;
26440
26441         return html ;
26442     },
26443     
26444     _insertHtmlBefore : function( html, element )
26445     {
26446         if ( element.insertAdjacentHTML )       {
26447             // IE
26448             element.insertAdjacentHTML( 'beforeBegin', html ) ;
26449         } else { // Gecko
26450             var oRange = document.createRange() ;
26451             oRange.setStartBefore( element ) ;
26452             var oFragment = oRange.createContextualFragment( html );
26453             element.parentNode.insertBefore( oFragment, element ) ;
26454         }
26455     }
26456     
26457     
26458   
26459     
26460     
26461     
26462     
26463
26464 });
26465
26466 //Roo.reg('fckeditor', Roo.form.FCKeditor);
26467
26468 function FCKeditor_OnComplete(editorInstance){
26469     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
26470     f.fckEditor = editorInstance;
26471     //console.log("loaded");
26472     f.fireEvent('editorinit', f, editorInstance);
26473
26474   
26475
26476  
26477
26478
26479
26480
26481
26482
26483
26484
26485
26486
26487
26488
26489
26490
26491
26492 //<script type="text/javascript">
26493 /**
26494  * @class Roo.form.GridField
26495  * @extends Roo.form.Field
26496  * Embed a grid (or editable grid into a form)
26497  * STATUS ALPHA
26498  * 
26499  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
26500  * it needs 
26501  * xgrid.store = Roo.data.Store
26502  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
26503  * xgrid.store.reader = Roo.data.JsonReader 
26504  * 
26505  * 
26506  * @constructor
26507  * Creates a new GridField
26508  * @param {Object} config Configuration options
26509  */
26510 Roo.form.GridField = function(config){
26511     Roo.form.GridField.superclass.constructor.call(this, config);
26512      
26513 };
26514
26515 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
26516     /**
26517      * @cfg {Number} width  - used to restrict width of grid..
26518      */
26519     width : 100,
26520     /**
26521      * @cfg {Number} height - used to restrict height of grid..
26522      */
26523     height : 50,
26524      /**
26525      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
26526          * 
26527          *}
26528      */
26529     xgrid : false, 
26530     /**
26531      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
26532      * {tag: "input", type: "checkbox", autocomplete: "off"})
26533      */
26534    // defaultAutoCreate : { tag: 'div' },
26535     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
26536     /**
26537      * @cfg {String} addTitle Text to include for adding a title.
26538      */
26539     addTitle : false,
26540     //
26541     onResize : function(){
26542         Roo.form.Field.superclass.onResize.apply(this, arguments);
26543     },
26544
26545     initEvents : function(){
26546         // Roo.form.Checkbox.superclass.initEvents.call(this);
26547         // has no events...
26548        
26549     },
26550
26551
26552     getResizeEl : function(){
26553         return this.wrap;
26554     },
26555
26556     getPositionEl : function(){
26557         return this.wrap;
26558     },
26559
26560     // private
26561     onRender : function(ct, position){
26562         
26563         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
26564         var style = this.style;
26565         delete this.style;
26566         
26567         Roo.form.GridField.superclass.onRender.call(this, ct, position);
26568         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
26569         this.viewEl = this.wrap.createChild({ tag: 'div' });
26570         if (style) {
26571             this.viewEl.applyStyles(style);
26572         }
26573         if (this.width) {
26574             this.viewEl.setWidth(this.width);
26575         }
26576         if (this.height) {
26577             this.viewEl.setHeight(this.height);
26578         }
26579         //if(this.inputValue !== undefined){
26580         //this.setValue(this.value);
26581         
26582         
26583         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
26584         
26585         
26586         this.grid.render();
26587         this.grid.getDataSource().on('remove', this.refreshValue, this);
26588         this.grid.getDataSource().on('update', this.refreshValue, this);
26589         this.grid.on('afteredit', this.refreshValue, this);
26590  
26591     },
26592      
26593     
26594     /**
26595      * Sets the value of the item. 
26596      * @param {String} either an object  or a string..
26597      */
26598     setValue : function(v){
26599         //this.value = v;
26600         v = v || []; // empty set..
26601         // this does not seem smart - it really only affects memoryproxy grids..
26602         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
26603             var ds = this.grid.getDataSource();
26604             // assumes a json reader..
26605             var data = {}
26606             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
26607             ds.loadData( data);
26608         }
26609         // clear selection so it does not get stale.
26610         if (this.grid.sm) { 
26611             this.grid.sm.clearSelections();
26612         }
26613         
26614         Roo.form.GridField.superclass.setValue.call(this, v);
26615         this.refreshValue();
26616         // should load data in the grid really....
26617     },
26618     
26619     // private
26620     refreshValue: function() {
26621          var val = [];
26622         this.grid.getDataSource().each(function(r) {
26623             val.push(r.data);
26624         });
26625         this.el.dom.value = Roo.encode(val);
26626     }
26627     
26628      
26629     
26630     
26631 });/*
26632  * Based on:
26633  * Ext JS Library 1.1.1
26634  * Copyright(c) 2006-2007, Ext JS, LLC.
26635  *
26636  * Originally Released Under LGPL - original licence link has changed is not relivant.
26637  *
26638  * Fork - LGPL
26639  * <script type="text/javascript">
26640  */
26641 /**
26642  * @class Roo.form.DisplayField
26643  * @extends Roo.form.Field
26644  * A generic Field to display non-editable data.
26645  * @cfg {Boolean} closable (true|false) default false
26646  * @constructor
26647  * Creates a new Display Field item.
26648  * @param {Object} config Configuration options
26649  */
26650 Roo.form.DisplayField = function(config){
26651     Roo.form.DisplayField.superclass.constructor.call(this, config);
26652     
26653     this.addEvents({
26654         /**
26655          * @event close
26656          * Fires after the click the close btn
26657              * @param {Roo.form.DisplayField} this
26658              */
26659         close : true
26660     });
26661 };
26662
26663 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
26664     inputType:      'hidden',
26665     allowBlank:     true,
26666     readOnly:         true,
26667     
26668  
26669     /**
26670      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
26671      */
26672     focusClass : undefined,
26673     /**
26674      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
26675      */
26676     fieldClass: 'x-form-field',
26677     
26678      /**
26679      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
26680      */
26681     valueRenderer: undefined,
26682     
26683     width: 100,
26684     /**
26685      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
26686      * {tag: "input", type: "checkbox", autocomplete: "off"})
26687      */
26688      
26689  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
26690  
26691     closable : false,
26692     
26693     onResize : function(){
26694         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
26695         
26696     },
26697
26698     initEvents : function(){
26699         // Roo.form.Checkbox.superclass.initEvents.call(this);
26700         // has no events...
26701         
26702         if(this.closable){
26703             this.closeEl.on('click', this.onClose, this);
26704         }
26705        
26706     },
26707
26708
26709     getResizeEl : function(){
26710         return this.wrap;
26711     },
26712
26713     getPositionEl : function(){
26714         return this.wrap;
26715     },
26716
26717     // private
26718     onRender : function(ct, position){
26719         
26720         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
26721         //if(this.inputValue !== undefined){
26722         this.wrap = this.el.wrap();
26723         
26724         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
26725         
26726         if(this.closable){
26727             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
26728         }
26729         
26730         if (this.bodyStyle) {
26731             this.viewEl.applyStyles(this.bodyStyle);
26732         }
26733         //this.viewEl.setStyle('padding', '2px');
26734         
26735         this.setValue(this.value);
26736         
26737     },
26738 /*
26739     // private
26740     initValue : Roo.emptyFn,
26741
26742   */
26743
26744         // private
26745     onClick : function(){
26746         
26747     },
26748
26749     /**
26750      * Sets the checked state of the checkbox.
26751      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
26752      */
26753     setValue : function(v){
26754         this.value = v;
26755         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
26756         // this might be called before we have a dom element..
26757         if (!this.viewEl) {
26758             return;
26759         }
26760         this.viewEl.dom.innerHTML = html;
26761         Roo.form.DisplayField.superclass.setValue.call(this, v);
26762
26763     },
26764     
26765     onClose : function(e)
26766     {
26767         e.preventDefault();
26768         
26769         this.fireEvent('close', this);
26770     }
26771 });/*
26772  * 
26773  * Licence- LGPL
26774  * 
26775  */
26776
26777 /**
26778  * @class Roo.form.DayPicker
26779  * @extends Roo.form.Field
26780  * A Day picker show [M] [T] [W] ....
26781  * @constructor
26782  * Creates a new Day Picker
26783  * @param {Object} config Configuration options
26784  */
26785 Roo.form.DayPicker= function(config){
26786     Roo.form.DayPicker.superclass.constructor.call(this, config);
26787      
26788 };
26789
26790 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
26791     /**
26792      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
26793      */
26794     focusClass : undefined,
26795     /**
26796      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
26797      */
26798     fieldClass: "x-form-field",
26799    
26800     /**
26801      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
26802      * {tag: "input", type: "checkbox", autocomplete: "off"})
26803      */
26804     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
26805     
26806    
26807     actionMode : 'viewEl', 
26808     //
26809     // private
26810  
26811     inputType : 'hidden',
26812     
26813      
26814     inputElement: false, // real input element?
26815     basedOn: false, // ????
26816     
26817     isFormField: true, // not sure where this is needed!!!!
26818
26819     onResize : function(){
26820         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
26821         if(!this.boxLabel){
26822             this.el.alignTo(this.wrap, 'c-c');
26823         }
26824     },
26825
26826     initEvents : function(){
26827         Roo.form.Checkbox.superclass.initEvents.call(this);
26828         this.el.on("click", this.onClick,  this);
26829         this.el.on("change", this.onClick,  this);
26830     },
26831
26832
26833     getResizeEl : function(){
26834         return this.wrap;
26835     },
26836
26837     getPositionEl : function(){
26838         return this.wrap;
26839     },
26840
26841     
26842     // private
26843     onRender : function(ct, position){
26844         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
26845        
26846         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
26847         
26848         var r1 = '<table><tr>';
26849         var r2 = '<tr class="x-form-daypick-icons">';
26850         for (var i=0; i < 7; i++) {
26851             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
26852             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
26853         }
26854         
26855         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
26856         viewEl.select('img').on('click', this.onClick, this);
26857         this.viewEl = viewEl;   
26858         
26859         
26860         // this will not work on Chrome!!!
26861         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
26862         this.el.on('propertychange', this.setFromHidden,  this);  //ie
26863         
26864         
26865           
26866
26867     },
26868
26869     // private
26870     initValue : Roo.emptyFn,
26871
26872     /**
26873      * Returns the checked state of the checkbox.
26874      * @return {Boolean} True if checked, else false
26875      */
26876     getValue : function(){
26877         return this.el.dom.value;
26878         
26879     },
26880
26881         // private
26882     onClick : function(e){ 
26883         //this.setChecked(!this.checked);
26884         Roo.get(e.target).toggleClass('x-menu-item-checked');
26885         this.refreshValue();
26886         //if(this.el.dom.checked != this.checked){
26887         //    this.setValue(this.el.dom.checked);
26888        // }
26889     },
26890     
26891     // private
26892     refreshValue : function()
26893     {
26894         var val = '';
26895         this.viewEl.select('img',true).each(function(e,i,n)  {
26896             val += e.is(".x-menu-item-checked") ? String(n) : '';
26897         });
26898         this.setValue(val, true);
26899     },
26900
26901     /**
26902      * Sets the checked state of the checkbox.
26903      * On is always based on a string comparison between inputValue and the param.
26904      * @param {Boolean/String} value - the value to set 
26905      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
26906      */
26907     setValue : function(v,suppressEvent){
26908         if (!this.el.dom) {
26909             return;
26910         }
26911         var old = this.el.dom.value ;
26912         this.el.dom.value = v;
26913         if (suppressEvent) {
26914             return ;
26915         }
26916          
26917         // update display..
26918         this.viewEl.select('img',true).each(function(e,i,n)  {
26919             
26920             var on = e.is(".x-menu-item-checked");
26921             var newv = v.indexOf(String(n)) > -1;
26922             if (on != newv) {
26923                 e.toggleClass('x-menu-item-checked');
26924             }
26925             
26926         });
26927         
26928         
26929         this.fireEvent('change', this, v, old);
26930         
26931         
26932     },
26933    
26934     // handle setting of hidden value by some other method!!?!?
26935     setFromHidden: function()
26936     {
26937         if(!this.el){
26938             return;
26939         }
26940         //console.log("SET FROM HIDDEN");
26941         //alert('setFrom hidden');
26942         this.setValue(this.el.dom.value);
26943     },
26944     
26945     onDestroy : function()
26946     {
26947         if(this.viewEl){
26948             Roo.get(this.viewEl).remove();
26949         }
26950          
26951         Roo.form.DayPicker.superclass.onDestroy.call(this);
26952     }
26953
26954 });/*
26955  * RooJS Library 1.1.1
26956  * Copyright(c) 2008-2011  Alan Knowles
26957  *
26958  * License - LGPL
26959  */
26960  
26961
26962 /**
26963  * @class Roo.form.ComboCheck
26964  * @extends Roo.form.ComboBox
26965  * A combobox for multiple select items.
26966  *
26967  * FIXME - could do with a reset button..
26968  * 
26969  * @constructor
26970  * Create a new ComboCheck
26971  * @param {Object} config Configuration options
26972  */
26973 Roo.form.ComboCheck = function(config){
26974     Roo.form.ComboCheck.superclass.constructor.call(this, config);
26975     // should verify some data...
26976     // like
26977     // hiddenName = required..
26978     // displayField = required
26979     // valudField == required
26980     var req= [ 'hiddenName', 'displayField', 'valueField' ];
26981     var _t = this;
26982     Roo.each(req, function(e) {
26983         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
26984             throw "Roo.form.ComboCheck : missing value for: " + e;
26985         }
26986     });
26987     
26988     
26989 };
26990
26991 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
26992      
26993      
26994     editable : false,
26995      
26996     selectedClass: 'x-menu-item-checked', 
26997     
26998     // private
26999     onRender : function(ct, position){
27000         var _t = this;
27001         
27002         
27003         
27004         if(!this.tpl){
27005             var cls = 'x-combo-list';
27006
27007             
27008             this.tpl =  new Roo.Template({
27009                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
27010                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
27011                    '<span>{' + this.displayField + '}</span>' +
27012                     '</div>' 
27013                 
27014             });
27015         }
27016  
27017         
27018         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
27019         this.view.singleSelect = false;
27020         this.view.multiSelect = true;
27021         this.view.toggleSelect = true;
27022         this.pageTb.add(new Roo.Toolbar.Fill(), {
27023             
27024             text: 'Done',
27025             handler: function()
27026             {
27027                 _t.collapse();
27028             }
27029         });
27030     },
27031     
27032     onViewOver : function(e, t){
27033         // do nothing...
27034         return;
27035         
27036     },
27037     
27038     onViewClick : function(doFocus,index){
27039         return;
27040         
27041     },
27042     select: function () {
27043         //Roo.log("SELECT CALLED");
27044     },
27045      
27046     selectByValue : function(xv, scrollIntoView){
27047         var ar = this.getValueArray();
27048         var sels = [];
27049         
27050         Roo.each(ar, function(v) {
27051             if(v === undefined || v === null){
27052                 return;
27053             }
27054             var r = this.findRecord(this.valueField, v);
27055             if(r){
27056                 sels.push(this.store.indexOf(r))
27057                 
27058             }
27059         },this);
27060         this.view.select(sels);
27061         return false;
27062     },
27063     
27064     
27065     
27066     onSelect : function(record, index){
27067        // Roo.log("onselect Called");
27068        // this is only called by the clear button now..
27069         this.view.clearSelections();
27070         this.setValue('[]');
27071         if (this.value != this.valueBefore) {
27072             this.fireEvent('change', this, this.value, this.valueBefore);
27073             this.valueBefore = this.value;
27074         }
27075     },
27076     getValueArray : function()
27077     {
27078         var ar = [] ;
27079         
27080         try {
27081             //Roo.log(this.value);
27082             if (typeof(this.value) == 'undefined') {
27083                 return [];
27084             }
27085             var ar = Roo.decode(this.value);
27086             return  ar instanceof Array ? ar : []; //?? valid?
27087             
27088         } catch(e) {
27089             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
27090             return [];
27091         }
27092          
27093     },
27094     expand : function ()
27095     {
27096         
27097         Roo.form.ComboCheck.superclass.expand.call(this);
27098         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
27099         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
27100         
27101
27102     },
27103     
27104     collapse : function(){
27105         Roo.form.ComboCheck.superclass.collapse.call(this);
27106         var sl = this.view.getSelectedIndexes();
27107         var st = this.store;
27108         var nv = [];
27109         var tv = [];
27110         var r;
27111         Roo.each(sl, function(i) {
27112             r = st.getAt(i);
27113             nv.push(r.get(this.valueField));
27114         },this);
27115         this.setValue(Roo.encode(nv));
27116         if (this.value != this.valueBefore) {
27117
27118             this.fireEvent('change', this, this.value, this.valueBefore);
27119             this.valueBefore = this.value;
27120         }
27121         
27122     },
27123     
27124     setValue : function(v){
27125         // Roo.log(v);
27126         this.value = v;
27127         
27128         var vals = this.getValueArray();
27129         var tv = [];
27130         Roo.each(vals, function(k) {
27131             var r = this.findRecord(this.valueField, k);
27132             if(r){
27133                 tv.push(r.data[this.displayField]);
27134             }else if(this.valueNotFoundText !== undefined){
27135                 tv.push( this.valueNotFoundText );
27136             }
27137         },this);
27138        // Roo.log(tv);
27139         
27140         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
27141         this.hiddenField.value = v;
27142         this.value = v;
27143     }
27144     
27145 });/*
27146  * Based on:
27147  * Ext JS Library 1.1.1
27148  * Copyright(c) 2006-2007, Ext JS, LLC.
27149  *
27150  * Originally Released Under LGPL - original licence link has changed is not relivant.
27151  *
27152  * Fork - LGPL
27153  * <script type="text/javascript">
27154  */
27155  
27156 /**
27157  * @class Roo.form.Signature
27158  * @extends Roo.form.Field
27159  * Signature field.  
27160  * @constructor
27161  * 
27162  * @param {Object} config Configuration options
27163  */
27164
27165 Roo.form.Signature = function(config){
27166     Roo.form.Signature.superclass.constructor.call(this, config);
27167     
27168     this.addEvents({// not in used??
27169          /**
27170          * @event confirm
27171          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
27172              * @param {Roo.form.Signature} combo This combo box
27173              */
27174         'confirm' : true,
27175         /**
27176          * @event reset
27177          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
27178              * @param {Roo.form.ComboBox} combo This combo box
27179              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
27180              */
27181         'reset' : true
27182     });
27183 };
27184
27185 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
27186     /**
27187      * @cfg {Object} labels Label to use when rendering a form.
27188      * defaults to 
27189      * labels : { 
27190      *      clear : "Clear",
27191      *      confirm : "Confirm"
27192      *  }
27193      */
27194     labels : { 
27195         clear : "Clear",
27196         confirm : "Confirm"
27197     },
27198     /**
27199      * @cfg {Number} width The signature panel width (defaults to 300)
27200      */
27201     width: 300,
27202     /**
27203      * @cfg {Number} height The signature panel height (defaults to 100)
27204      */
27205     height : 100,
27206     /**
27207      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
27208      */
27209     allowBlank : false,
27210     
27211     //private
27212     // {Object} signPanel The signature SVG panel element (defaults to {})
27213     signPanel : {},
27214     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
27215     isMouseDown : false,
27216     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
27217     isConfirmed : false,
27218     // {String} signatureTmp SVG mapping string (defaults to empty string)
27219     signatureTmp : '',
27220     
27221     
27222     defaultAutoCreate : { // modified by initCompnoent..
27223         tag: "input",
27224         type:"hidden"
27225     },
27226
27227     // private
27228     onRender : function(ct, position){
27229         
27230         Roo.form.Signature.superclass.onRender.call(this, ct, position);
27231         
27232         this.wrap = this.el.wrap({
27233             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
27234         });
27235         
27236         this.createToolbar(this);
27237         this.signPanel = this.wrap.createChild({
27238                 tag: 'div',
27239                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
27240             }, this.el
27241         );
27242             
27243         this.svgID = Roo.id();
27244         this.svgEl = this.signPanel.createChild({
27245               xmlns : 'http://www.w3.org/2000/svg',
27246               tag : 'svg',
27247               id : this.svgID + "-svg",
27248               width: this.width,
27249               height: this.height,
27250               viewBox: '0 0 '+this.width+' '+this.height,
27251               cn : [
27252                 {
27253                     tag: "rect",
27254                     id: this.svgID + "-svg-r",
27255                     width: this.width,
27256                     height: this.height,
27257                     fill: "#ffa"
27258                 },
27259                 {
27260                     tag: "line",
27261                     id: this.svgID + "-svg-l",
27262                     x1: "0", // start
27263                     y1: (this.height*0.8), // start set the line in 80% of height
27264                     x2: this.width, // end
27265                     y2: (this.height*0.8), // end set the line in 80% of height
27266                     'stroke': "#666",
27267                     'stroke-width': "1",
27268                     'stroke-dasharray': "3",
27269                     'shape-rendering': "crispEdges",
27270                     'pointer-events': "none"
27271                 },
27272                 {
27273                     tag: "path",
27274                     id: this.svgID + "-svg-p",
27275                     'stroke': "navy",
27276                     'stroke-width': "3",
27277                     'fill': "none",
27278                     'pointer-events': 'none'
27279                 }
27280               ]
27281         });
27282         this.createSVG();
27283         this.svgBox = this.svgEl.dom.getScreenCTM();
27284     },
27285     createSVG : function(){ 
27286         var svg = this.signPanel;
27287         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
27288         var t = this;
27289
27290         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
27291         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
27292         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
27293         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
27294         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
27295         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
27296         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
27297         
27298     },
27299     isTouchEvent : function(e){
27300         return e.type.match(/^touch/);
27301     },
27302     getCoords : function (e) {
27303         var pt    = this.svgEl.dom.createSVGPoint();
27304         pt.x = e.clientX; 
27305         pt.y = e.clientY;
27306         if (this.isTouchEvent(e)) {
27307             pt.x =  e.targetTouches[0].clientX;
27308             pt.y = e.targetTouches[0].clientY;
27309         }
27310         var a = this.svgEl.dom.getScreenCTM();
27311         var b = a.inverse();
27312         var mx = pt.matrixTransform(b);
27313         return mx.x + ',' + mx.y;
27314     },
27315     //mouse event headler 
27316     down : function (e) {
27317         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
27318         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
27319         
27320         this.isMouseDown = true;
27321         
27322         e.preventDefault();
27323     },
27324     move : function (e) {
27325         if (this.isMouseDown) {
27326             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
27327             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
27328         }
27329         
27330         e.preventDefault();
27331     },
27332     up : function (e) {
27333         this.isMouseDown = false;
27334         var sp = this.signatureTmp.split(' ');
27335         
27336         if(sp.length > 1){
27337             if(!sp[sp.length-2].match(/^L/)){
27338                 sp.pop();
27339                 sp.pop();
27340                 sp.push("");
27341                 this.signatureTmp = sp.join(" ");
27342             }
27343         }
27344         if(this.getValue() != this.signatureTmp){
27345             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27346             this.isConfirmed = false;
27347         }
27348         e.preventDefault();
27349     },
27350     
27351     /**
27352      * Protected method that will not generally be called directly. It
27353      * is called when the editor creates its toolbar. Override this method if you need to
27354      * add custom toolbar buttons.
27355      * @param {HtmlEditor} editor
27356      */
27357     createToolbar : function(editor){
27358          function btn(id, toggle, handler){
27359             var xid = fid + '-'+ id ;
27360             return {
27361                 id : xid,
27362                 cmd : id,
27363                 cls : 'x-btn-icon x-edit-'+id,
27364                 enableToggle:toggle !== false,
27365                 scope: editor, // was editor...
27366                 handler:handler||editor.relayBtnCmd,
27367                 clickEvent:'mousedown',
27368                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27369                 tabIndex:-1
27370             };
27371         }
27372         
27373         
27374         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
27375         this.tb = tb;
27376         this.tb.add(
27377            {
27378                 cls : ' x-signature-btn x-signature-'+id,
27379                 scope: editor, // was editor...
27380                 handler: this.reset,
27381                 clickEvent:'mousedown',
27382                 text: this.labels.clear
27383             },
27384             {
27385                  xtype : 'Fill',
27386                  xns: Roo.Toolbar
27387             }, 
27388             {
27389                 cls : '  x-signature-btn x-signature-'+id,
27390                 scope: editor, // was editor...
27391                 handler: this.confirmHandler,
27392                 clickEvent:'mousedown',
27393                 text: this.labels.confirm
27394             }
27395         );
27396     
27397     },
27398     //public
27399     /**
27400      * when user is clicked confirm then show this image.....
27401      * 
27402      * @return {String} Image Data URI
27403      */
27404     getImageDataURI : function(){
27405         var svg = this.svgEl.dom.parentNode.innerHTML;
27406         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
27407         return src; 
27408     },
27409     /**
27410      * 
27411      * @return {Boolean} this.isConfirmed
27412      */
27413     getConfirmed : function(){
27414         return this.isConfirmed;
27415     },
27416     /**
27417      * 
27418      * @return {Number} this.width
27419      */
27420     getWidth : function(){
27421         return this.width;
27422     },
27423     /**
27424      * 
27425      * @return {Number} this.height
27426      */
27427     getHeight : function(){
27428         return this.height;
27429     },
27430     // private
27431     getSignature : function(){
27432         return this.signatureTmp;
27433     },
27434     // private
27435     reset : function(){
27436         this.signatureTmp = '';
27437         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27438         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
27439         this.isConfirmed = false;
27440         Roo.form.Signature.superclass.reset.call(this);
27441     },
27442     setSignature : function(s){
27443         this.signatureTmp = s;
27444         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27445         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
27446         this.setValue(s);
27447         this.isConfirmed = false;
27448         Roo.form.Signature.superclass.reset.call(this);
27449     }, 
27450     test : function(){
27451 //        Roo.log(this.signPanel.dom.contentWindow.up())
27452     },
27453     //private
27454     setConfirmed : function(){
27455         
27456         
27457         
27458 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
27459     },
27460     // private
27461     confirmHandler : function(){
27462         if(!this.getSignature()){
27463             return;
27464         }
27465         
27466         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
27467         this.setValue(this.getSignature());
27468         this.isConfirmed = true;
27469         
27470         this.fireEvent('confirm', this);
27471     },
27472     // private
27473     // Subclasses should provide the validation implementation by overriding this
27474     validateValue : function(value){
27475         if(this.allowBlank){
27476             return true;
27477         }
27478         
27479         if(this.isConfirmed){
27480             return true;
27481         }
27482         return false;
27483     }
27484 });/*
27485  * Based on:
27486  * Ext JS Library 1.1.1
27487  * Copyright(c) 2006-2007, Ext JS, LLC.
27488  *
27489  * Originally Released Under LGPL - original licence link has changed is not relivant.
27490  *
27491  * Fork - LGPL
27492  * <script type="text/javascript">
27493  */
27494  
27495
27496 /**
27497  * @class Roo.form.ComboBox
27498  * @extends Roo.form.TriggerField
27499  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
27500  * @constructor
27501  * Create a new ComboBox.
27502  * @param {Object} config Configuration options
27503  */
27504 Roo.form.Select = function(config){
27505     Roo.form.Select.superclass.constructor.call(this, config);
27506      
27507 };
27508
27509 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
27510     /**
27511      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
27512      */
27513     /**
27514      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
27515      * rendering into an Roo.Editor, defaults to false)
27516      */
27517     /**
27518      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
27519      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
27520      */
27521     /**
27522      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
27523      */
27524     /**
27525      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
27526      * the dropdown list (defaults to undefined, with no header element)
27527      */
27528
27529      /**
27530      * @cfg {String/Roo.Template} tpl The template to use to render the output
27531      */
27532      
27533     // private
27534     defaultAutoCreate : {tag: "select"  },
27535     /**
27536      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
27537      */
27538     listWidth: undefined,
27539     /**
27540      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
27541      * mode = 'remote' or 'text' if mode = 'local')
27542      */
27543     displayField: undefined,
27544     /**
27545      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
27546      * mode = 'remote' or 'value' if mode = 'local'). 
27547      * Note: use of a valueField requires the user make a selection
27548      * in order for a value to be mapped.
27549      */
27550     valueField: undefined,
27551     
27552     
27553     /**
27554      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
27555      * field's data value (defaults to the underlying DOM element's name)
27556      */
27557     hiddenName: undefined,
27558     /**
27559      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
27560      */
27561     listClass: '',
27562     /**
27563      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
27564      */
27565     selectedClass: 'x-combo-selected',
27566     /**
27567      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
27568      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
27569      * which displays a downward arrow icon).
27570      */
27571     triggerClass : 'x-form-arrow-trigger',
27572     /**
27573      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
27574      */
27575     shadow:'sides',
27576     /**
27577      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
27578      * anchor positions (defaults to 'tl-bl')
27579      */
27580     listAlign: 'tl-bl?',
27581     /**
27582      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
27583      */
27584     maxHeight: 300,
27585     /**
27586      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
27587      * query specified by the allQuery config option (defaults to 'query')
27588      */
27589     triggerAction: 'query',
27590     /**
27591      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
27592      * (defaults to 4, does not apply if editable = false)
27593      */
27594     minChars : 4,
27595     /**
27596      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
27597      * delay (typeAheadDelay) if it matches a known value (defaults to false)
27598      */
27599     typeAhead: false,
27600     /**
27601      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
27602      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
27603      */
27604     queryDelay: 500,
27605     /**
27606      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
27607      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
27608      */
27609     pageSize: 0,
27610     /**
27611      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
27612      * when editable = true (defaults to false)
27613      */
27614     selectOnFocus:false,
27615     /**
27616      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
27617      */
27618     queryParam: 'query',
27619     /**
27620      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
27621      * when mode = 'remote' (defaults to 'Loading...')
27622      */
27623     loadingText: 'Loading...',
27624     /**
27625      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
27626      */
27627     resizable: false,
27628     /**
27629      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
27630      */
27631     handleHeight : 8,
27632     /**
27633      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
27634      * traditional select (defaults to true)
27635      */
27636     editable: true,
27637     /**
27638      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
27639      */
27640     allQuery: '',
27641     /**
27642      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
27643      */
27644     mode: 'remote',
27645     /**
27646      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
27647      * listWidth has a higher value)
27648      */
27649     minListWidth : 70,
27650     /**
27651      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
27652      * allow the user to set arbitrary text into the field (defaults to false)
27653      */
27654     forceSelection:false,
27655     /**
27656      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
27657      * if typeAhead = true (defaults to 250)
27658      */
27659     typeAheadDelay : 250,
27660     /**
27661      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
27662      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
27663      */
27664     valueNotFoundText : undefined,
27665     
27666     /**
27667      * @cfg {String} defaultValue The value displayed after loading the store.
27668      */
27669     defaultValue: '',
27670     
27671     /**
27672      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
27673      */
27674     blockFocus : false,
27675     
27676     /**
27677      * @cfg {Boolean} disableClear Disable showing of clear button.
27678      */
27679     disableClear : false,
27680     /**
27681      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
27682      */
27683     alwaysQuery : false,
27684     
27685     //private
27686     addicon : false,
27687     editicon: false,
27688     
27689     // element that contains real text value.. (when hidden is used..)
27690      
27691     // private
27692     onRender : function(ct, position){
27693         Roo.form.Field.prototype.onRender.call(this, ct, position);
27694         
27695         if(this.store){
27696             this.store.on('beforeload', this.onBeforeLoad, this);
27697             this.store.on('load', this.onLoad, this);
27698             this.store.on('loadexception', this.onLoadException, this);
27699             this.store.load({});
27700         }
27701         
27702         
27703         
27704     },
27705
27706     // private
27707     initEvents : function(){
27708         //Roo.form.ComboBox.superclass.initEvents.call(this);
27709  
27710     },
27711
27712     onDestroy : function(){
27713        
27714         if(this.store){
27715             this.store.un('beforeload', this.onBeforeLoad, this);
27716             this.store.un('load', this.onLoad, this);
27717             this.store.un('loadexception', this.onLoadException, this);
27718         }
27719         //Roo.form.ComboBox.superclass.onDestroy.call(this);
27720     },
27721
27722     // private
27723     fireKey : function(e){
27724         if(e.isNavKeyPress() && !this.list.isVisible()){
27725             this.fireEvent("specialkey", this, e);
27726         }
27727     },
27728
27729     // private
27730     onResize: function(w, h){
27731         
27732         return; 
27733     
27734         
27735     },
27736
27737     /**
27738      * Allow or prevent the user from directly editing the field text.  If false is passed,
27739      * the user will only be able to select from the items defined in the dropdown list.  This method
27740      * is the runtime equivalent of setting the 'editable' config option at config time.
27741      * @param {Boolean} value True to allow the user to directly edit the field text
27742      */
27743     setEditable : function(value){
27744          
27745     },
27746
27747     // private
27748     onBeforeLoad : function(){
27749         
27750         Roo.log("Select before load");
27751         return;
27752     
27753         this.innerList.update(this.loadingText ?
27754                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
27755         //this.restrictHeight();
27756         this.selectedIndex = -1;
27757     },
27758
27759     // private
27760     onLoad : function(){
27761
27762     
27763         var dom = this.el.dom;
27764         dom.innerHTML = '';
27765          var od = dom.ownerDocument;
27766          
27767         if (this.emptyText) {
27768             var op = od.createElement('option');
27769             op.setAttribute('value', '');
27770             op.innerHTML = String.format('{0}', this.emptyText);
27771             dom.appendChild(op);
27772         }
27773         if(this.store.getCount() > 0){
27774            
27775             var vf = this.valueField;
27776             var df = this.displayField;
27777             this.store.data.each(function(r) {
27778                 // which colmsn to use... testing - cdoe / title..
27779                 var op = od.createElement('option');
27780                 op.setAttribute('value', r.data[vf]);
27781                 op.innerHTML = String.format('{0}', r.data[df]);
27782                 dom.appendChild(op);
27783             });
27784             if (typeof(this.defaultValue != 'undefined')) {
27785                 this.setValue(this.defaultValue);
27786             }
27787             
27788              
27789         }else{
27790             //this.onEmptyResults();
27791         }
27792         //this.el.focus();
27793     },
27794     // private
27795     onLoadException : function()
27796     {
27797         dom.innerHTML = '';
27798             
27799         Roo.log("Select on load exception");
27800         return;
27801     
27802         this.collapse();
27803         Roo.log(this.store.reader.jsonData);
27804         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
27805             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
27806         }
27807         
27808         
27809     },
27810     // private
27811     onTypeAhead : function(){
27812          
27813     },
27814
27815     // private
27816     onSelect : function(record, index){
27817         Roo.log('on select?');
27818         return;
27819         if(this.fireEvent('beforeselect', this, record, index) !== false){
27820             this.setFromData(index > -1 ? record.data : false);
27821             this.collapse();
27822             this.fireEvent('select', this, record, index);
27823         }
27824     },
27825
27826     /**
27827      * Returns the currently selected field value or empty string if no value is set.
27828      * @return {String} value The selected value
27829      */
27830     getValue : function(){
27831         var dom = this.el.dom;
27832         this.value = dom.options[dom.selectedIndex].value;
27833         return this.value;
27834         
27835     },
27836
27837     /**
27838      * Clears any text/value currently set in the field
27839      */
27840     clearValue : function(){
27841         this.value = '';
27842         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
27843         
27844     },
27845
27846     /**
27847      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
27848      * will be displayed in the field.  If the value does not match the data value of an existing item,
27849      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
27850      * Otherwise the field will be blank (although the value will still be set).
27851      * @param {String} value The value to match
27852      */
27853     setValue : function(v){
27854         var d = this.el.dom;
27855         for (var i =0; i < d.options.length;i++) {
27856             if (v == d.options[i].value) {
27857                 d.selectedIndex = i;
27858                 this.value = v;
27859                 return;
27860             }
27861         }
27862         this.clearValue();
27863     },
27864     /**
27865      * @property {Object} the last set data for the element
27866      */
27867     
27868     lastData : false,
27869     /**
27870      * Sets the value of the field based on a object which is related to the record format for the store.
27871      * @param {Object} value the value to set as. or false on reset?
27872      */
27873     setFromData : function(o){
27874         Roo.log('setfrom data?');
27875          
27876         
27877         
27878     },
27879     // private
27880     reset : function(){
27881         this.clearValue();
27882     },
27883     // private
27884     findRecord : function(prop, value){
27885         
27886         return false;
27887     
27888         var record;
27889         if(this.store.getCount() > 0){
27890             this.store.each(function(r){
27891                 if(r.data[prop] == value){
27892                     record = r;
27893                     return false;
27894                 }
27895                 return true;
27896             });
27897         }
27898         return record;
27899     },
27900     
27901     getName: function()
27902     {
27903         // returns hidden if it's set..
27904         if (!this.rendered) {return ''};
27905         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
27906         
27907     },
27908      
27909
27910     
27911
27912     // private
27913     onEmptyResults : function(){
27914         Roo.log('empty results');
27915         //this.collapse();
27916     },
27917
27918     /**
27919      * Returns true if the dropdown list is expanded, else false.
27920      */
27921     isExpanded : function(){
27922         return false;
27923     },
27924
27925     /**
27926      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
27927      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
27928      * @param {String} value The data value of the item to select
27929      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
27930      * selected item if it is not currently in view (defaults to true)
27931      * @return {Boolean} True if the value matched an item in the list, else false
27932      */
27933     selectByValue : function(v, scrollIntoView){
27934         Roo.log('select By Value');
27935         return false;
27936     
27937         if(v !== undefined && v !== null){
27938             var r = this.findRecord(this.valueField || this.displayField, v);
27939             if(r){
27940                 this.select(this.store.indexOf(r), scrollIntoView);
27941                 return true;
27942             }
27943         }
27944         return false;
27945     },
27946
27947     /**
27948      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
27949      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
27950      * @param {Number} index The zero-based index of the list item to select
27951      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
27952      * selected item if it is not currently in view (defaults to true)
27953      */
27954     select : function(index, scrollIntoView){
27955         Roo.log('select ');
27956         return  ;
27957         
27958         this.selectedIndex = index;
27959         this.view.select(index);
27960         if(scrollIntoView !== false){
27961             var el = this.view.getNode(index);
27962             if(el){
27963                 this.innerList.scrollChildIntoView(el, false);
27964             }
27965         }
27966     },
27967
27968       
27969
27970     // private
27971     validateBlur : function(){
27972         
27973         return;
27974         
27975     },
27976
27977     // private
27978     initQuery : function(){
27979         this.doQuery(this.getRawValue());
27980     },
27981
27982     // private
27983     doForce : function(){
27984         if(this.el.dom.value.length > 0){
27985             this.el.dom.value =
27986                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
27987              
27988         }
27989     },
27990
27991     /**
27992      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
27993      * query allowing the query action to be canceled if needed.
27994      * @param {String} query The SQL query to execute
27995      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
27996      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
27997      * saved in the current store (defaults to false)
27998      */
27999     doQuery : function(q, forceAll){
28000         
28001         Roo.log('doQuery?');
28002         if(q === undefined || q === null){
28003             q = '';
28004         }
28005         var qe = {
28006             query: q,
28007             forceAll: forceAll,
28008             combo: this,
28009             cancel:false
28010         };
28011         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
28012             return false;
28013         }
28014         q = qe.query;
28015         forceAll = qe.forceAll;
28016         if(forceAll === true || (q.length >= this.minChars)){
28017             if(this.lastQuery != q || this.alwaysQuery){
28018                 this.lastQuery = q;
28019                 if(this.mode == 'local'){
28020                     this.selectedIndex = -1;
28021                     if(forceAll){
28022                         this.store.clearFilter();
28023                     }else{
28024                         this.store.filter(this.displayField, q);
28025                     }
28026                     this.onLoad();
28027                 }else{
28028                     this.store.baseParams[this.queryParam] = q;
28029                     this.store.load({
28030                         params: this.getParams(q)
28031                     });
28032                     this.expand();
28033                 }
28034             }else{
28035                 this.selectedIndex = -1;
28036                 this.onLoad();   
28037             }
28038         }
28039     },
28040
28041     // private
28042     getParams : function(q){
28043         var p = {};
28044         //p[this.queryParam] = q;
28045         if(this.pageSize){
28046             p.start = 0;
28047             p.limit = this.pageSize;
28048         }
28049         return p;
28050     },
28051
28052     /**
28053      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
28054      */
28055     collapse : function(){
28056         
28057     },
28058
28059     // private
28060     collapseIf : function(e){
28061         
28062     },
28063
28064     /**
28065      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
28066      */
28067     expand : function(){
28068         
28069     } ,
28070
28071     // private
28072      
28073
28074     /** 
28075     * @cfg {Boolean} grow 
28076     * @hide 
28077     */
28078     /** 
28079     * @cfg {Number} growMin 
28080     * @hide 
28081     */
28082     /** 
28083     * @cfg {Number} growMax 
28084     * @hide 
28085     */
28086     /**
28087      * @hide
28088      * @method autoSize
28089      */
28090     
28091     setWidth : function()
28092     {
28093         
28094     },
28095     getResizeEl : function(){
28096         return this.el;
28097     }
28098 });//<script type="text/javasscript">
28099  
28100
28101 /**
28102  * @class Roo.DDView
28103  * A DnD enabled version of Roo.View.
28104  * @param {Element/String} container The Element in which to create the View.
28105  * @param {String} tpl The template string used to create the markup for each element of the View
28106  * @param {Object} config The configuration properties. These include all the config options of
28107  * {@link Roo.View} plus some specific to this class.<br>
28108  * <p>
28109  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
28110  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
28111  * <p>
28112  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
28113 .x-view-drag-insert-above {
28114         border-top:1px dotted #3366cc;
28115 }
28116 .x-view-drag-insert-below {
28117         border-bottom:1px dotted #3366cc;
28118 }
28119 </code></pre>
28120  * 
28121  */
28122  
28123 Roo.DDView = function(container, tpl, config) {
28124     Roo.DDView.superclass.constructor.apply(this, arguments);
28125     this.getEl().setStyle("outline", "0px none");
28126     this.getEl().unselectable();
28127     if (this.dragGroup) {
28128                 this.setDraggable(this.dragGroup.split(","));
28129     }
28130     if (this.dropGroup) {
28131                 this.setDroppable(this.dropGroup.split(","));
28132     }
28133     if (this.deletable) {
28134         this.setDeletable();
28135     }
28136     this.isDirtyFlag = false;
28137         this.addEvents({
28138                 "drop" : true
28139         });
28140 };
28141
28142 Roo.extend(Roo.DDView, Roo.View, {
28143 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
28144 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
28145 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
28146 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
28147
28148         isFormField: true,
28149
28150         reset: Roo.emptyFn,
28151         
28152         clearInvalid: Roo.form.Field.prototype.clearInvalid,
28153
28154         validate: function() {
28155                 return true;
28156         },
28157         
28158         destroy: function() {
28159                 this.purgeListeners();
28160                 this.getEl.removeAllListeners();
28161                 this.getEl().remove();
28162                 if (this.dragZone) {
28163                         if (this.dragZone.destroy) {
28164                                 this.dragZone.destroy();
28165                         }
28166                 }
28167                 if (this.dropZone) {
28168                         if (this.dropZone.destroy) {
28169                                 this.dropZone.destroy();
28170                         }
28171                 }
28172         },
28173
28174 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
28175         getName: function() {
28176                 return this.name;
28177         },
28178
28179 /**     Loads the View from a JSON string representing the Records to put into the Store. */
28180         setValue: function(v) {
28181                 if (!this.store) {
28182                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
28183                 }
28184                 var data = {};
28185                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
28186                 this.store.proxy = new Roo.data.MemoryProxy(data);
28187                 this.store.load();
28188         },
28189
28190 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
28191         getValue: function() {
28192                 var result = '(';
28193                 this.store.each(function(rec) {
28194                         result += rec.id + ',';
28195                 });
28196                 return result.substr(0, result.length - 1) + ')';
28197         },
28198         
28199         getIds: function() {
28200                 var i = 0, result = new Array(this.store.getCount());
28201                 this.store.each(function(rec) {
28202                         result[i++] = rec.id;
28203                 });
28204                 return result;
28205         },
28206         
28207         isDirty: function() {
28208                 return this.isDirtyFlag;
28209         },
28210
28211 /**
28212  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
28213  *      whole Element becomes the target, and this causes the drop gesture to append.
28214  */
28215     getTargetFromEvent : function(e) {
28216                 var target = e.getTarget();
28217                 while ((target !== null) && (target.parentNode != this.el.dom)) {
28218                 target = target.parentNode;
28219                 }
28220                 if (!target) {
28221                         target = this.el.dom.lastChild || this.el.dom;
28222                 }
28223                 return target;
28224     },
28225
28226 /**
28227  *      Create the drag data which consists of an object which has the property "ddel" as
28228  *      the drag proxy element. 
28229  */
28230     getDragData : function(e) {
28231         var target = this.findItemFromChild(e.getTarget());
28232                 if(target) {
28233                         this.handleSelection(e);
28234                         var selNodes = this.getSelectedNodes();
28235             var dragData = {
28236                 source: this,
28237                 copy: this.copy || (this.allowCopy && e.ctrlKey),
28238                 nodes: selNodes,
28239                 records: []
28240                         };
28241                         var selectedIndices = this.getSelectedIndexes();
28242                         for (var i = 0; i < selectedIndices.length; i++) {
28243                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
28244                         }
28245                         if (selNodes.length == 1) {
28246                                 dragData.ddel = target.cloneNode(true); // the div element
28247                         } else {
28248                                 var div = document.createElement('div'); // create the multi element drag "ghost"
28249                                 div.className = 'multi-proxy';
28250                                 for (var i = 0, len = selNodes.length; i < len; i++) {
28251                                         div.appendChild(selNodes[i].cloneNode(true));
28252                                 }
28253                                 dragData.ddel = div;
28254                         }
28255             //console.log(dragData)
28256             //console.log(dragData.ddel.innerHTML)
28257                         return dragData;
28258                 }
28259         //console.log('nodragData')
28260                 return false;
28261     },
28262     
28263 /**     Specify to which ddGroup items in this DDView may be dragged. */
28264     setDraggable: function(ddGroup) {
28265         if (ddGroup instanceof Array) {
28266                 Roo.each(ddGroup, this.setDraggable, this);
28267                 return;
28268         }
28269         if (this.dragZone) {
28270                 this.dragZone.addToGroup(ddGroup);
28271         } else {
28272                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
28273                                 containerScroll: true,
28274                                 ddGroup: ddGroup 
28275
28276                         });
28277 //                      Draggability implies selection. DragZone's mousedown selects the element.
28278                         if (!this.multiSelect) { this.singleSelect = true; }
28279
28280 //                      Wire the DragZone's handlers up to methods in *this*
28281                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
28282                 }
28283     },
28284
28285 /**     Specify from which ddGroup this DDView accepts drops. */
28286     setDroppable: function(ddGroup) {
28287         if (ddGroup instanceof Array) {
28288                 Roo.each(ddGroup, this.setDroppable, this);
28289                 return;
28290         }
28291         if (this.dropZone) {
28292                 this.dropZone.addToGroup(ddGroup);
28293         } else {
28294                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
28295                                 containerScroll: true,
28296                                 ddGroup: ddGroup
28297                         });
28298
28299 //                      Wire the DropZone's handlers up to methods in *this*
28300                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
28301                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
28302                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
28303                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
28304                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
28305                 }
28306     },
28307
28308 /**     Decide whether to drop above or below a View node. */
28309     getDropPoint : function(e, n, dd){
28310         if (n == this.el.dom) { return "above"; }
28311                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
28312                 var c = t + (b - t) / 2;
28313                 var y = Roo.lib.Event.getPageY(e);
28314                 if(y <= c) {
28315                         return "above";
28316                 }else{
28317                         return "below";
28318                 }
28319     },
28320
28321     onNodeEnter : function(n, dd, e, data){
28322                 return false;
28323     },
28324     
28325     onNodeOver : function(n, dd, e, data){
28326                 var pt = this.getDropPoint(e, n, dd);
28327                 // set the insert point style on the target node
28328                 var dragElClass = this.dropNotAllowed;
28329                 if (pt) {
28330                         var targetElClass;
28331                         if (pt == "above"){
28332                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
28333                                 targetElClass = "x-view-drag-insert-above";
28334                         } else {
28335                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
28336                                 targetElClass = "x-view-drag-insert-below";
28337                         }
28338                         if (this.lastInsertClass != targetElClass){
28339                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
28340                                 this.lastInsertClass = targetElClass;
28341                         }
28342                 }
28343                 return dragElClass;
28344         },
28345
28346     onNodeOut : function(n, dd, e, data){
28347                 this.removeDropIndicators(n);
28348     },
28349
28350     onNodeDrop : function(n, dd, e, data){
28351         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
28352                 return false;
28353         }
28354         var pt = this.getDropPoint(e, n, dd);
28355                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
28356                 if (pt == "below") { insertAt++; }
28357                 for (var i = 0; i < data.records.length; i++) {
28358                         var r = data.records[i];
28359                         var dup = this.store.getById(r.id);
28360                         if (dup && (dd != this.dragZone)) {
28361                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
28362                         } else {
28363                                 if (data.copy) {
28364                                         this.store.insert(insertAt++, r.copy());
28365                                 } else {
28366                                         data.source.isDirtyFlag = true;
28367                                         r.store.remove(r);
28368                                         this.store.insert(insertAt++, r);
28369                                 }
28370                                 this.isDirtyFlag = true;
28371                         }
28372                 }
28373                 this.dragZone.cachedTarget = null;
28374                 return true;
28375     },
28376
28377     removeDropIndicators : function(n){
28378                 if(n){
28379                         Roo.fly(n).removeClass([
28380                                 "x-view-drag-insert-above",
28381                                 "x-view-drag-insert-below"]);
28382                         this.lastInsertClass = "_noclass";
28383                 }
28384     },
28385
28386 /**
28387  *      Utility method. Add a delete option to the DDView's context menu.
28388  *      @param {String} imageUrl The URL of the "delete" icon image.
28389  */
28390         setDeletable: function(imageUrl) {
28391                 if (!this.singleSelect && !this.multiSelect) {
28392                         this.singleSelect = true;
28393                 }
28394                 var c = this.getContextMenu();
28395                 this.contextMenu.on("itemclick", function(item) {
28396                         switch (item.id) {
28397                                 case "delete":
28398                                         this.remove(this.getSelectedIndexes());
28399                                         break;
28400                         }
28401                 }, this);
28402                 this.contextMenu.add({
28403                         icon: imageUrl,
28404                         id: "delete",
28405                         text: 'Delete'
28406                 });
28407         },
28408         
28409 /**     Return the context menu for this DDView. */
28410         getContextMenu: function() {
28411                 if (!this.contextMenu) {
28412 //                      Create the View's context menu
28413                         this.contextMenu = new Roo.menu.Menu({
28414                                 id: this.id + "-contextmenu"
28415                         });
28416                         this.el.on("contextmenu", this.showContextMenu, this);
28417                 }
28418                 return this.contextMenu;
28419         },
28420         
28421         disableContextMenu: function() {
28422                 if (this.contextMenu) {
28423                         this.el.un("contextmenu", this.showContextMenu, this);
28424                 }
28425         },
28426
28427         showContextMenu: function(e, item) {
28428         item = this.findItemFromChild(e.getTarget());
28429                 if (item) {
28430                         e.stopEvent();
28431                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
28432                         this.contextMenu.showAt(e.getXY());
28433             }
28434     },
28435
28436 /**
28437  *      Remove {@link Roo.data.Record}s at the specified indices.
28438  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
28439  */
28440     remove: function(selectedIndices) {
28441                 selectedIndices = [].concat(selectedIndices);
28442                 for (var i = 0; i < selectedIndices.length; i++) {
28443                         var rec = this.store.getAt(selectedIndices[i]);
28444                         this.store.remove(rec);
28445                 }
28446     },
28447
28448 /**
28449  *      Double click fires the event, but also, if this is draggable, and there is only one other
28450  *      related DropZone, it transfers the selected node.
28451  */
28452     onDblClick : function(e){
28453         var item = this.findItemFromChild(e.getTarget());
28454         if(item){
28455             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
28456                 return false;
28457             }
28458             if (this.dragGroup) {
28459                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
28460                     while (targets.indexOf(this.dropZone) > -1) {
28461                             targets.remove(this.dropZone);
28462                                 }
28463                     if (targets.length == 1) {
28464                                         this.dragZone.cachedTarget = null;
28465                         var el = Roo.get(targets[0].getEl());
28466                         var box = el.getBox(true);
28467                         targets[0].onNodeDrop(el.dom, {
28468                                 target: el.dom,
28469                                 xy: [box.x, box.y + box.height - 1]
28470                         }, null, this.getDragData(e));
28471                     }
28472                 }
28473         }
28474     },
28475     
28476     handleSelection: function(e) {
28477                 this.dragZone.cachedTarget = null;
28478         var item = this.findItemFromChild(e.getTarget());
28479         if (!item) {
28480                 this.clearSelections(true);
28481                 return;
28482         }
28483                 if (item && (this.multiSelect || this.singleSelect)){
28484                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
28485                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
28486                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
28487                                 this.unselect(item);
28488                         } else {
28489                                 this.select(item, this.multiSelect && e.ctrlKey);
28490                                 this.lastSelection = item;
28491                         }
28492                 }
28493     },
28494
28495     onItemClick : function(item, index, e){
28496                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
28497                         return false;
28498                 }
28499                 return true;
28500     },
28501
28502     unselect : function(nodeInfo, suppressEvent){
28503                 var node = this.getNode(nodeInfo);
28504                 if(node && this.isSelected(node)){
28505                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28506                                 Roo.fly(node).removeClass(this.selectedClass);
28507                                 this.selections.remove(node);
28508                                 if(!suppressEvent){
28509                                         this.fireEvent("selectionchange", this, this.selections);
28510                                 }
28511                         }
28512                 }
28513     }
28514 });
28515 /*
28516  * Based on:
28517  * Ext JS Library 1.1.1
28518  * Copyright(c) 2006-2007, Ext JS, LLC.
28519  *
28520  * Originally Released Under LGPL - original licence link has changed is not relivant.
28521  *
28522  * Fork - LGPL
28523  * <script type="text/javascript">
28524  */
28525  
28526 /**
28527  * @class Roo.LayoutManager
28528  * @extends Roo.util.Observable
28529  * Base class for layout managers.
28530  */
28531 Roo.LayoutManager = function(container, config){
28532     Roo.LayoutManager.superclass.constructor.call(this);
28533     this.el = Roo.get(container);
28534     // ie scrollbar fix
28535     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
28536         document.body.scroll = "no";
28537     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
28538         this.el.position('relative');
28539     }
28540     this.id = this.el.id;
28541     this.el.addClass("x-layout-container");
28542     /** false to disable window resize monitoring @type Boolean */
28543     this.monitorWindowResize = true;
28544     this.regions = {};
28545     this.addEvents({
28546         /**
28547          * @event layout
28548          * Fires when a layout is performed. 
28549          * @param {Roo.LayoutManager} this
28550          */
28551         "layout" : true,
28552         /**
28553          * @event regionresized
28554          * Fires when the user resizes a region. 
28555          * @param {Roo.LayoutRegion} region The resized region
28556          * @param {Number} newSize The new size (width for east/west, height for north/south)
28557          */
28558         "regionresized" : true,
28559         /**
28560          * @event regioncollapsed
28561          * Fires when a region is collapsed. 
28562          * @param {Roo.LayoutRegion} region The collapsed region
28563          */
28564         "regioncollapsed" : true,
28565         /**
28566          * @event regionexpanded
28567          * Fires when a region is expanded.  
28568          * @param {Roo.LayoutRegion} region The expanded region
28569          */
28570         "regionexpanded" : true
28571     });
28572     this.updating = false;
28573     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
28574 };
28575
28576 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
28577     /**
28578      * Returns true if this layout is currently being updated
28579      * @return {Boolean}
28580      */
28581     isUpdating : function(){
28582         return this.updating; 
28583     },
28584     
28585     /**
28586      * Suspend the LayoutManager from doing auto-layouts while
28587      * making multiple add or remove calls
28588      */
28589     beginUpdate : function(){
28590         this.updating = true;    
28591     },
28592     
28593     /**
28594      * Restore auto-layouts and optionally disable the manager from performing a layout
28595      * @param {Boolean} noLayout true to disable a layout update 
28596      */
28597     endUpdate : function(noLayout){
28598         this.updating = false;
28599         if(!noLayout){
28600             this.layout();
28601         }    
28602     },
28603     
28604     layout: function(){
28605         
28606     },
28607     
28608     onRegionResized : function(region, newSize){
28609         this.fireEvent("regionresized", region, newSize);
28610         this.layout();
28611     },
28612     
28613     onRegionCollapsed : function(region){
28614         this.fireEvent("regioncollapsed", region);
28615     },
28616     
28617     onRegionExpanded : function(region){
28618         this.fireEvent("regionexpanded", region);
28619     },
28620         
28621     /**
28622      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
28623      * performs box-model adjustments.
28624      * @return {Object} The size as an object {width: (the width), height: (the height)}
28625      */
28626     getViewSize : function(){
28627         var size;
28628         if(this.el.dom != document.body){
28629             size = this.el.getSize();
28630         }else{
28631             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
28632         }
28633         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
28634         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
28635         return size;
28636     },
28637     
28638     /**
28639      * Returns the Element this layout is bound to.
28640      * @return {Roo.Element}
28641      */
28642     getEl : function(){
28643         return this.el;
28644     },
28645     
28646     /**
28647      * Returns the specified region.
28648      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
28649      * @return {Roo.LayoutRegion}
28650      */
28651     getRegion : function(target){
28652         return this.regions[target.toLowerCase()];
28653     },
28654     
28655     onWindowResize : function(){
28656         if(this.monitorWindowResize){
28657             this.layout();
28658         }
28659     }
28660 });/*
28661  * Based on:
28662  * Ext JS Library 1.1.1
28663  * Copyright(c) 2006-2007, Ext JS, LLC.
28664  *
28665  * Originally Released Under LGPL - original licence link has changed is not relivant.
28666  *
28667  * Fork - LGPL
28668  * <script type="text/javascript">
28669  */
28670 /**
28671  * @class Roo.BorderLayout
28672  * @extends Roo.LayoutManager
28673  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
28674  * please see: <br><br>
28675  * <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>
28676  * <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>
28677  * Example:
28678  <pre><code>
28679  var layout = new Roo.BorderLayout(document.body, {
28680     north: {
28681         initialSize: 25,
28682         titlebar: false
28683     },
28684     west: {
28685         split:true,
28686         initialSize: 200,
28687         minSize: 175,
28688         maxSize: 400,
28689         titlebar: true,
28690         collapsible: true
28691     },
28692     east: {
28693         split:true,
28694         initialSize: 202,
28695         minSize: 175,
28696         maxSize: 400,
28697         titlebar: true,
28698         collapsible: true
28699     },
28700     south: {
28701         split:true,
28702         initialSize: 100,
28703         minSize: 100,
28704         maxSize: 200,
28705         titlebar: true,
28706         collapsible: true
28707     },
28708     center: {
28709         titlebar: true,
28710         autoScroll:true,
28711         resizeTabs: true,
28712         minTabWidth: 50,
28713         preferredTabWidth: 150
28714     }
28715 });
28716
28717 // shorthand
28718 var CP = Roo.ContentPanel;
28719
28720 layout.beginUpdate();
28721 layout.add("north", new CP("north", "North"));
28722 layout.add("south", new CP("south", {title: "South", closable: true}));
28723 layout.add("west", new CP("west", {title: "West"}));
28724 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
28725 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
28726 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
28727 layout.getRegion("center").showPanel("center1");
28728 layout.endUpdate();
28729 </code></pre>
28730
28731 <b>The container the layout is rendered into can be either the body element or any other element.
28732 If it is not the body element, the container needs to either be an absolute positioned element,
28733 or you will need to add "position:relative" to the css of the container.  You will also need to specify
28734 the container size if it is not the body element.</b>
28735
28736 * @constructor
28737 * Create a new BorderLayout
28738 * @param {String/HTMLElement/Element} container The container this layout is bound to
28739 * @param {Object} config Configuration options
28740  */
28741 Roo.BorderLayout = function(container, config){
28742     config = config || {};
28743     Roo.BorderLayout.superclass.constructor.call(this, container, config);
28744     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
28745     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
28746         var target = this.factory.validRegions[i];
28747         if(config[target]){
28748             this.addRegion(target, config[target]);
28749         }
28750     }
28751 };
28752
28753 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
28754     /**
28755      * Creates and adds a new region if it doesn't already exist.
28756      * @param {String} target The target region key (north, south, east, west or center).
28757      * @param {Object} config The regions config object
28758      * @return {BorderLayoutRegion} The new region
28759      */
28760     addRegion : function(target, config){
28761         if(!this.regions[target]){
28762             var r = this.factory.create(target, this, config);
28763             this.bindRegion(target, r);
28764         }
28765         return this.regions[target];
28766     },
28767
28768     // private (kinda)
28769     bindRegion : function(name, r){
28770         this.regions[name] = r;
28771         r.on("visibilitychange", this.layout, this);
28772         r.on("paneladded", this.layout, this);
28773         r.on("panelremoved", this.layout, this);
28774         r.on("invalidated", this.layout, this);
28775         r.on("resized", this.onRegionResized, this);
28776         r.on("collapsed", this.onRegionCollapsed, this);
28777         r.on("expanded", this.onRegionExpanded, this);
28778     },
28779
28780     /**
28781      * Performs a layout update.
28782      */
28783     layout : function(){
28784         if(this.updating) {
28785             return;
28786         }
28787         var size = this.getViewSize();
28788         var w = size.width;
28789         var h = size.height;
28790         var centerW = w;
28791         var centerH = h;
28792         var centerY = 0;
28793         var centerX = 0;
28794         //var x = 0, y = 0;
28795
28796         var rs = this.regions;
28797         var north = rs["north"];
28798         var south = rs["south"]; 
28799         var west = rs["west"];
28800         var east = rs["east"];
28801         var center = rs["center"];
28802         //if(this.hideOnLayout){ // not supported anymore
28803             //c.el.setStyle("display", "none");
28804         //}
28805         if(north && north.isVisible()){
28806             var b = north.getBox();
28807             var m = north.getMargins();
28808             b.width = w - (m.left+m.right);
28809             b.x = m.left;
28810             b.y = m.top;
28811             centerY = b.height + b.y + m.bottom;
28812             centerH -= centerY;
28813             north.updateBox(this.safeBox(b));
28814         }
28815         if(south && south.isVisible()){
28816             var b = south.getBox();
28817             var m = south.getMargins();
28818             b.width = w - (m.left+m.right);
28819             b.x = m.left;
28820             var totalHeight = (b.height + m.top + m.bottom);
28821             b.y = h - totalHeight + m.top;
28822             centerH -= totalHeight;
28823             south.updateBox(this.safeBox(b));
28824         }
28825         if(west && west.isVisible()){
28826             var b = west.getBox();
28827             var m = west.getMargins();
28828             b.height = centerH - (m.top+m.bottom);
28829             b.x = m.left;
28830             b.y = centerY + m.top;
28831             var totalWidth = (b.width + m.left + m.right);
28832             centerX += totalWidth;
28833             centerW -= totalWidth;
28834             west.updateBox(this.safeBox(b));
28835         }
28836         if(east && east.isVisible()){
28837             var b = east.getBox();
28838             var m = east.getMargins();
28839             b.height = centerH - (m.top+m.bottom);
28840             var totalWidth = (b.width + m.left + m.right);
28841             b.x = w - totalWidth + m.left;
28842             b.y = centerY + m.top;
28843             centerW -= totalWidth;
28844             east.updateBox(this.safeBox(b));
28845         }
28846         if(center){
28847             var m = center.getMargins();
28848             var centerBox = {
28849                 x: centerX + m.left,
28850                 y: centerY + m.top,
28851                 width: centerW - (m.left+m.right),
28852                 height: centerH - (m.top+m.bottom)
28853             };
28854             //if(this.hideOnLayout){
28855                 //center.el.setStyle("display", "block");
28856             //}
28857             center.updateBox(this.safeBox(centerBox));
28858         }
28859         this.el.repaint();
28860         this.fireEvent("layout", this);
28861     },
28862
28863     // private
28864     safeBox : function(box){
28865         box.width = Math.max(0, box.width);
28866         box.height = Math.max(0, box.height);
28867         return box;
28868     },
28869
28870     /**
28871      * Adds a ContentPanel (or subclass) to this layout.
28872      * @param {String} target The target region key (north, south, east, west or center).
28873      * @param {Roo.ContentPanel} panel The panel to add
28874      * @return {Roo.ContentPanel} The added panel
28875      */
28876     add : function(target, panel){
28877          
28878         target = target.toLowerCase();
28879         return this.regions[target].add(panel);
28880     },
28881
28882     /**
28883      * Remove a ContentPanel (or subclass) to this layout.
28884      * @param {String} target The target region key (north, south, east, west or center).
28885      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
28886      * @return {Roo.ContentPanel} The removed panel
28887      */
28888     remove : function(target, panel){
28889         target = target.toLowerCase();
28890         return this.regions[target].remove(panel);
28891     },
28892
28893     /**
28894      * Searches all regions for a panel with the specified id
28895      * @param {String} panelId
28896      * @return {Roo.ContentPanel} The panel or null if it wasn't found
28897      */
28898     findPanel : function(panelId){
28899         var rs = this.regions;
28900         for(var target in rs){
28901             if(typeof rs[target] != "function"){
28902                 var p = rs[target].getPanel(panelId);
28903                 if(p){
28904                     return p;
28905                 }
28906             }
28907         }
28908         return null;
28909     },
28910
28911     /**
28912      * Searches all regions for a panel with the specified id and activates (shows) it.
28913      * @param {String/ContentPanel} panelId The panels id or the panel itself
28914      * @return {Roo.ContentPanel} The shown panel or null
28915      */
28916     showPanel : function(panelId) {
28917       var rs = this.regions;
28918       for(var target in rs){
28919          var r = rs[target];
28920          if(typeof r != "function"){
28921             if(r.hasPanel(panelId)){
28922                return r.showPanel(panelId);
28923             }
28924          }
28925       }
28926       return null;
28927    },
28928
28929    /**
28930      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
28931      * @param {Roo.state.Provider} provider (optional) An alternate state provider
28932      */
28933     restoreState : function(provider){
28934         if(!provider){
28935             provider = Roo.state.Manager;
28936         }
28937         var sm = new Roo.LayoutStateManager();
28938         sm.init(this, provider);
28939     },
28940
28941     /**
28942      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
28943      * object should contain properties for each region to add ContentPanels to, and each property's value should be
28944      * a valid ContentPanel config object.  Example:
28945      * <pre><code>
28946 // Create the main layout
28947 var layout = new Roo.BorderLayout('main-ct', {
28948     west: {
28949         split:true,
28950         minSize: 175,
28951         titlebar: true
28952     },
28953     center: {
28954         title:'Components'
28955     }
28956 }, 'main-ct');
28957
28958 // Create and add multiple ContentPanels at once via configs
28959 layout.batchAdd({
28960    west: {
28961        id: 'source-files',
28962        autoCreate:true,
28963        title:'Ext Source Files',
28964        autoScroll:true,
28965        fitToFrame:true
28966    },
28967    center : {
28968        el: cview,
28969        autoScroll:true,
28970        fitToFrame:true,
28971        toolbar: tb,
28972        resizeEl:'cbody'
28973    }
28974 });
28975 </code></pre>
28976      * @param {Object} regions An object containing ContentPanel configs by region name
28977      */
28978     batchAdd : function(regions){
28979         this.beginUpdate();
28980         for(var rname in regions){
28981             var lr = this.regions[rname];
28982             if(lr){
28983                 this.addTypedPanels(lr, regions[rname]);
28984             }
28985         }
28986         this.endUpdate();
28987     },
28988
28989     // private
28990     addTypedPanels : function(lr, ps){
28991         if(typeof ps == 'string'){
28992             lr.add(new Roo.ContentPanel(ps));
28993         }
28994         else if(ps instanceof Array){
28995             for(var i =0, len = ps.length; i < len; i++){
28996                 this.addTypedPanels(lr, ps[i]);
28997             }
28998         }
28999         else if(!ps.events){ // raw config?
29000             var el = ps.el;
29001             delete ps.el; // prevent conflict
29002             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
29003         }
29004         else {  // panel object assumed!
29005             lr.add(ps);
29006         }
29007     },
29008     /**
29009      * Adds a xtype elements to the layout.
29010      * <pre><code>
29011
29012 layout.addxtype({
29013        xtype : 'ContentPanel',
29014        region: 'west',
29015        items: [ .... ]
29016    }
29017 );
29018
29019 layout.addxtype({
29020         xtype : 'NestedLayoutPanel',
29021         region: 'west',
29022         layout: {
29023            center: { },
29024            west: { }   
29025         },
29026         items : [ ... list of content panels or nested layout panels.. ]
29027    }
29028 );
29029 </code></pre>
29030      * @param {Object} cfg Xtype definition of item to add.
29031      */
29032     addxtype : function(cfg)
29033     {
29034         // basically accepts a pannel...
29035         // can accept a layout region..!?!?
29036         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
29037         
29038         if (!cfg.xtype.match(/Panel$/)) {
29039             return false;
29040         }
29041         var ret = false;
29042         
29043         if (typeof(cfg.region) == 'undefined') {
29044             Roo.log("Failed to add Panel, region was not set");
29045             Roo.log(cfg);
29046             return false;
29047         }
29048         var region = cfg.region;
29049         delete cfg.region;
29050         
29051           
29052         var xitems = [];
29053         if (cfg.items) {
29054             xitems = cfg.items;
29055             delete cfg.items;
29056         }
29057         var nb = false;
29058         
29059         switch(cfg.xtype) 
29060         {
29061             case 'ContentPanel':  // ContentPanel (el, cfg)
29062             case 'ScrollPanel':  // ContentPanel (el, cfg)
29063             case 'ViewPanel': 
29064                 if(cfg.autoCreate) {
29065                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29066                 } else {
29067                     var el = this.el.createChild();
29068                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
29069                 }
29070                 
29071                 this.add(region, ret);
29072                 break;
29073             
29074             
29075             case 'TreePanel': // our new panel!
29076                 cfg.el = this.el.createChild();
29077                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29078                 this.add(region, ret);
29079                 break;
29080             
29081             case 'NestedLayoutPanel': 
29082                 // create a new Layout (which is  a Border Layout...
29083                 var el = this.el.createChild();
29084                 var clayout = cfg.layout;
29085                 delete cfg.layout;
29086                 clayout.items   = clayout.items  || [];
29087                 // replace this exitems with the clayout ones..
29088                 xitems = clayout.items;
29089                  
29090                 
29091                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
29092                     cfg.background = false;
29093                 }
29094                 var layout = new Roo.BorderLayout(el, clayout);
29095                 
29096                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
29097                 //console.log('adding nested layout panel '  + cfg.toSource());
29098                 this.add(region, ret);
29099                 nb = {}; /// find first...
29100                 break;
29101                 
29102             case 'GridPanel': 
29103             
29104                 // needs grid and region
29105                 
29106                 //var el = this.getRegion(region).el.createChild();
29107                 var el = this.el.createChild();
29108                 // create the grid first...
29109                 
29110                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
29111                 delete cfg.grid;
29112                 if (region == 'center' && this.active ) {
29113                     cfg.background = false;
29114                 }
29115                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
29116                 
29117                 this.add(region, ret);
29118                 if (cfg.background) {
29119                     ret.on('activate', function(gp) {
29120                         if (!gp.grid.rendered) {
29121                             gp.grid.render();
29122                         }
29123                     });
29124                 } else {
29125                     grid.render();
29126                 }
29127                 break;
29128            
29129            
29130            
29131                 
29132                 
29133                 
29134             default:
29135                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
29136                     
29137                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29138                     this.add(region, ret);
29139                 } else {
29140                 
29141                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
29142                     return null;
29143                 }
29144                 
29145              // GridPanel (grid, cfg)
29146             
29147         }
29148         this.beginUpdate();
29149         // add children..
29150         var region = '';
29151         var abn = {};
29152         Roo.each(xitems, function(i)  {
29153             region = nb && i.region ? i.region : false;
29154             
29155             var add = ret.addxtype(i);
29156            
29157             if (region) {
29158                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
29159                 if (!i.background) {
29160                     abn[region] = nb[region] ;
29161                 }
29162             }
29163             
29164         });
29165         this.endUpdate();
29166
29167         // make the last non-background panel active..
29168         //if (nb) { Roo.log(abn); }
29169         if (nb) {
29170             
29171             for(var r in abn) {
29172                 region = this.getRegion(r);
29173                 if (region) {
29174                     // tried using nb[r], but it does not work..
29175                      
29176                     region.showPanel(abn[r]);
29177                    
29178                 }
29179             }
29180         }
29181         return ret;
29182         
29183     }
29184 });
29185
29186 /**
29187  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
29188  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
29189  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
29190  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
29191  * <pre><code>
29192 // shorthand
29193 var CP = Roo.ContentPanel;
29194
29195 var layout = Roo.BorderLayout.create({
29196     north: {
29197         initialSize: 25,
29198         titlebar: false,
29199         panels: [new CP("north", "North")]
29200     },
29201     west: {
29202         split:true,
29203         initialSize: 200,
29204         minSize: 175,
29205         maxSize: 400,
29206         titlebar: true,
29207         collapsible: true,
29208         panels: [new CP("west", {title: "West"})]
29209     },
29210     east: {
29211         split:true,
29212         initialSize: 202,
29213         minSize: 175,
29214         maxSize: 400,
29215         titlebar: true,
29216         collapsible: true,
29217         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
29218     },
29219     south: {
29220         split:true,
29221         initialSize: 100,
29222         minSize: 100,
29223         maxSize: 200,
29224         titlebar: true,
29225         collapsible: true,
29226         panels: [new CP("south", {title: "South", closable: true})]
29227     },
29228     center: {
29229         titlebar: true,
29230         autoScroll:true,
29231         resizeTabs: true,
29232         minTabWidth: 50,
29233         preferredTabWidth: 150,
29234         panels: [
29235             new CP("center1", {title: "Close Me", closable: true}),
29236             new CP("center2", {title: "Center Panel", closable: false})
29237         ]
29238     }
29239 }, document.body);
29240
29241 layout.getRegion("center").showPanel("center1");
29242 </code></pre>
29243  * @param config
29244  * @param targetEl
29245  */
29246 Roo.BorderLayout.create = function(config, targetEl){
29247     var layout = new Roo.BorderLayout(targetEl || document.body, config);
29248     layout.beginUpdate();
29249     var regions = Roo.BorderLayout.RegionFactory.validRegions;
29250     for(var j = 0, jlen = regions.length; j < jlen; j++){
29251         var lr = regions[j];
29252         if(layout.regions[lr] && config[lr].panels){
29253             var r = layout.regions[lr];
29254             var ps = config[lr].panels;
29255             layout.addTypedPanels(r, ps);
29256         }
29257     }
29258     layout.endUpdate();
29259     return layout;
29260 };
29261
29262 // private
29263 Roo.BorderLayout.RegionFactory = {
29264     // private
29265     validRegions : ["north","south","east","west","center"],
29266
29267     // private
29268     create : function(target, mgr, config){
29269         target = target.toLowerCase();
29270         if(config.lightweight || config.basic){
29271             return new Roo.BasicLayoutRegion(mgr, config, target);
29272         }
29273         switch(target){
29274             case "north":
29275                 return new Roo.NorthLayoutRegion(mgr, config);
29276             case "south":
29277                 return new Roo.SouthLayoutRegion(mgr, config);
29278             case "east":
29279                 return new Roo.EastLayoutRegion(mgr, config);
29280             case "west":
29281                 return new Roo.WestLayoutRegion(mgr, config);
29282             case "center":
29283                 return new Roo.CenterLayoutRegion(mgr, config);
29284         }
29285         throw 'Layout region "'+target+'" not supported.';
29286     }
29287 };/*
29288  * Based on:
29289  * Ext JS Library 1.1.1
29290  * Copyright(c) 2006-2007, Ext JS, LLC.
29291  *
29292  * Originally Released Under LGPL - original licence link has changed is not relivant.
29293  *
29294  * Fork - LGPL
29295  * <script type="text/javascript">
29296  */
29297  
29298 /**
29299  * @class Roo.BasicLayoutRegion
29300  * @extends Roo.util.Observable
29301  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
29302  * and does not have a titlebar, tabs or any other features. All it does is size and position 
29303  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
29304  */
29305 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
29306     this.mgr = mgr;
29307     this.position  = pos;
29308     this.events = {
29309         /**
29310          * @scope Roo.BasicLayoutRegion
29311          */
29312         
29313         /**
29314          * @event beforeremove
29315          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
29316          * @param {Roo.LayoutRegion} this
29317          * @param {Roo.ContentPanel} panel The panel
29318          * @param {Object} e The cancel event object
29319          */
29320         "beforeremove" : true,
29321         /**
29322          * @event invalidated
29323          * Fires when the layout for this region is changed.
29324          * @param {Roo.LayoutRegion} this
29325          */
29326         "invalidated" : true,
29327         /**
29328          * @event visibilitychange
29329          * Fires when this region is shown or hidden 
29330          * @param {Roo.LayoutRegion} this
29331          * @param {Boolean} visibility true or false
29332          */
29333         "visibilitychange" : true,
29334         /**
29335          * @event paneladded
29336          * Fires when a panel is added. 
29337          * @param {Roo.LayoutRegion} this
29338          * @param {Roo.ContentPanel} panel The panel
29339          */
29340         "paneladded" : true,
29341         /**
29342          * @event panelremoved
29343          * Fires when a panel is removed. 
29344          * @param {Roo.LayoutRegion} this
29345          * @param {Roo.ContentPanel} panel The panel
29346          */
29347         "panelremoved" : true,
29348         /**
29349          * @event beforecollapse
29350          * Fires when this region before collapse.
29351          * @param {Roo.LayoutRegion} this
29352          */
29353         "beforecollapse" : true,
29354         /**
29355          * @event collapsed
29356          * Fires when this region is collapsed.
29357          * @param {Roo.LayoutRegion} this
29358          */
29359         "collapsed" : true,
29360         /**
29361          * @event expanded
29362          * Fires when this region is expanded.
29363          * @param {Roo.LayoutRegion} this
29364          */
29365         "expanded" : true,
29366         /**
29367          * @event slideshow
29368          * Fires when this region is slid into view.
29369          * @param {Roo.LayoutRegion} this
29370          */
29371         "slideshow" : true,
29372         /**
29373          * @event slidehide
29374          * Fires when this region slides out of view. 
29375          * @param {Roo.LayoutRegion} this
29376          */
29377         "slidehide" : true,
29378         /**
29379          * @event panelactivated
29380          * Fires when a panel is activated. 
29381          * @param {Roo.LayoutRegion} this
29382          * @param {Roo.ContentPanel} panel The activated panel
29383          */
29384         "panelactivated" : true,
29385         /**
29386          * @event resized
29387          * Fires when the user resizes this region. 
29388          * @param {Roo.LayoutRegion} this
29389          * @param {Number} newSize The new size (width for east/west, height for north/south)
29390          */
29391         "resized" : true
29392     };
29393     /** A collection of panels in this region. @type Roo.util.MixedCollection */
29394     this.panels = new Roo.util.MixedCollection();
29395     this.panels.getKey = this.getPanelId.createDelegate(this);
29396     this.box = null;
29397     this.activePanel = null;
29398     // ensure listeners are added...
29399     
29400     if (config.listeners || config.events) {
29401         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
29402             listeners : config.listeners || {},
29403             events : config.events || {}
29404         });
29405     }
29406     
29407     if(skipConfig !== true){
29408         this.applyConfig(config);
29409     }
29410 };
29411
29412 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
29413     getPanelId : function(p){
29414         return p.getId();
29415     },
29416     
29417     applyConfig : function(config){
29418         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29419         this.config = config;
29420         
29421     },
29422     
29423     /**
29424      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
29425      * the width, for horizontal (north, south) the height.
29426      * @param {Number} newSize The new width or height
29427      */
29428     resizeTo : function(newSize){
29429         var el = this.el ? this.el :
29430                  (this.activePanel ? this.activePanel.getEl() : null);
29431         if(el){
29432             switch(this.position){
29433                 case "east":
29434                 case "west":
29435                     el.setWidth(newSize);
29436                     this.fireEvent("resized", this, newSize);
29437                 break;
29438                 case "north":
29439                 case "south":
29440                     el.setHeight(newSize);
29441                     this.fireEvent("resized", this, newSize);
29442                 break;                
29443             }
29444         }
29445     },
29446     
29447     getBox : function(){
29448         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
29449     },
29450     
29451     getMargins : function(){
29452         return this.margins;
29453     },
29454     
29455     updateBox : function(box){
29456         this.box = box;
29457         var el = this.activePanel.getEl();
29458         el.dom.style.left = box.x + "px";
29459         el.dom.style.top = box.y + "px";
29460         this.activePanel.setSize(box.width, box.height);
29461     },
29462     
29463     /**
29464      * Returns the container element for this region.
29465      * @return {Roo.Element}
29466      */
29467     getEl : function(){
29468         return this.activePanel;
29469     },
29470     
29471     /**
29472      * Returns true if this region is currently visible.
29473      * @return {Boolean}
29474      */
29475     isVisible : function(){
29476         return this.activePanel ? true : false;
29477     },
29478     
29479     setActivePanel : function(panel){
29480         panel = this.getPanel(panel);
29481         if(this.activePanel && this.activePanel != panel){
29482             this.activePanel.setActiveState(false);
29483             this.activePanel.getEl().setLeftTop(-10000,-10000);
29484         }
29485         this.activePanel = panel;
29486         panel.setActiveState(true);
29487         if(this.box){
29488             panel.setSize(this.box.width, this.box.height);
29489         }
29490         this.fireEvent("panelactivated", this, panel);
29491         this.fireEvent("invalidated");
29492     },
29493     
29494     /**
29495      * Show the specified panel.
29496      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
29497      * @return {Roo.ContentPanel} The shown panel or null
29498      */
29499     showPanel : function(panel){
29500         if(panel = this.getPanel(panel)){
29501             this.setActivePanel(panel);
29502         }
29503         return panel;
29504     },
29505     
29506     /**
29507      * Get the active panel for this region.
29508      * @return {Roo.ContentPanel} The active panel or null
29509      */
29510     getActivePanel : function(){
29511         return this.activePanel;
29512     },
29513     
29514     /**
29515      * Add the passed ContentPanel(s)
29516      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
29517      * @return {Roo.ContentPanel} The panel added (if only one was added)
29518      */
29519     add : function(panel){
29520         if(arguments.length > 1){
29521             for(var i = 0, len = arguments.length; i < len; i++) {
29522                 this.add(arguments[i]);
29523             }
29524             return null;
29525         }
29526         if(this.hasPanel(panel)){
29527             this.showPanel(panel);
29528             return panel;
29529         }
29530         var el = panel.getEl();
29531         if(el.dom.parentNode != this.mgr.el.dom){
29532             this.mgr.el.dom.appendChild(el.dom);
29533         }
29534         if(panel.setRegion){
29535             panel.setRegion(this);
29536         }
29537         this.panels.add(panel);
29538         el.setStyle("position", "absolute");
29539         if(!panel.background){
29540             this.setActivePanel(panel);
29541             if(this.config.initialSize && this.panels.getCount()==1){
29542                 this.resizeTo(this.config.initialSize);
29543             }
29544         }
29545         this.fireEvent("paneladded", this, panel);
29546         return panel;
29547     },
29548     
29549     /**
29550      * Returns true if the panel is in this region.
29551      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29552      * @return {Boolean}
29553      */
29554     hasPanel : function(panel){
29555         if(typeof panel == "object"){ // must be panel obj
29556             panel = panel.getId();
29557         }
29558         return this.getPanel(panel) ? true : false;
29559     },
29560     
29561     /**
29562      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
29563      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29564      * @param {Boolean} preservePanel Overrides the config preservePanel option
29565      * @return {Roo.ContentPanel} The panel that was removed
29566      */
29567     remove : function(panel, preservePanel){
29568         panel = this.getPanel(panel);
29569         if(!panel){
29570             return null;
29571         }
29572         var e = {};
29573         this.fireEvent("beforeremove", this, panel, e);
29574         if(e.cancel === true){
29575             return null;
29576         }
29577         var panelId = panel.getId();
29578         this.panels.removeKey(panelId);
29579         return panel;
29580     },
29581     
29582     /**
29583      * Returns the panel specified or null if it's not in this region.
29584      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29585      * @return {Roo.ContentPanel}
29586      */
29587     getPanel : function(id){
29588         if(typeof id == "object"){ // must be panel obj
29589             return id;
29590         }
29591         return this.panels.get(id);
29592     },
29593     
29594     /**
29595      * Returns this regions position (north/south/east/west/center).
29596      * @return {String} 
29597      */
29598     getPosition: function(){
29599         return this.position;    
29600     }
29601 });/*
29602  * Based on:
29603  * Ext JS Library 1.1.1
29604  * Copyright(c) 2006-2007, Ext JS, LLC.
29605  *
29606  * Originally Released Under LGPL - original licence link has changed is not relivant.
29607  *
29608  * Fork - LGPL
29609  * <script type="text/javascript">
29610  */
29611  
29612 /**
29613  * @class Roo.LayoutRegion
29614  * @extends Roo.BasicLayoutRegion
29615  * This class represents a region in a layout manager.
29616  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
29617  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
29618  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
29619  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
29620  * @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})
29621  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
29622  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
29623  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
29624  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
29625  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
29626  * @cfg {String}    title           The title for the region (overrides panel titles)
29627  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
29628  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
29629  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
29630  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
29631  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
29632  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
29633  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
29634  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
29635  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
29636  * @cfg {Boolean}   showPin         True to show a pin button
29637  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
29638  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
29639  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
29640  * @cfg {Number}    width           For East/West panels
29641  * @cfg {Number}    height          For North/South panels
29642  * @cfg {Boolean}   split           To show the splitter
29643  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
29644  */
29645 Roo.LayoutRegion = function(mgr, config, pos){
29646     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
29647     var dh = Roo.DomHelper;
29648     /** This region's container element 
29649     * @type Roo.Element */
29650     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
29651     /** This region's title element 
29652     * @type Roo.Element */
29653
29654     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
29655         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
29656         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
29657     ]}, true);
29658     this.titleEl.enableDisplayMode();
29659     /** This region's title text element 
29660     * @type HTMLElement */
29661     this.titleTextEl = this.titleEl.dom.firstChild;
29662     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
29663     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
29664     this.closeBtn.enableDisplayMode();
29665     this.closeBtn.on("click", this.closeClicked, this);
29666     this.closeBtn.hide();
29667
29668     this.createBody(config);
29669     this.visible = true;
29670     this.collapsed = false;
29671
29672     if(config.hideWhenEmpty){
29673         this.hide();
29674         this.on("paneladded", this.validateVisibility, this);
29675         this.on("panelremoved", this.validateVisibility, this);
29676     }
29677     this.applyConfig(config);
29678 };
29679
29680 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
29681
29682     createBody : function(){
29683         /** This region's body element 
29684         * @type Roo.Element */
29685         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
29686     },
29687
29688     applyConfig : function(c){
29689         if(c.collapsible && this.position != "center" && !this.collapsedEl){
29690             var dh = Roo.DomHelper;
29691             if(c.titlebar !== false){
29692                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
29693                 this.collapseBtn.on("click", this.collapse, this);
29694                 this.collapseBtn.enableDisplayMode();
29695
29696                 if(c.showPin === true || this.showPin){
29697                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
29698                     this.stickBtn.enableDisplayMode();
29699                     this.stickBtn.on("click", this.expand, this);
29700                     this.stickBtn.hide();
29701                 }
29702             }
29703             /** This region's collapsed element
29704             * @type Roo.Element */
29705             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
29706                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
29707             ]}, true);
29708             if(c.floatable !== false){
29709                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
29710                this.collapsedEl.on("click", this.collapseClick, this);
29711             }
29712
29713             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
29714                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
29715                    id: "message", unselectable: "on", style:{"float":"left"}});
29716                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
29717              }
29718             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
29719             this.expandBtn.on("click", this.expand, this);
29720         }
29721         if(this.collapseBtn){
29722             this.collapseBtn.setVisible(c.collapsible == true);
29723         }
29724         this.cmargins = c.cmargins || this.cmargins ||
29725                          (this.position == "west" || this.position == "east" ?
29726                              {top: 0, left: 2, right:2, bottom: 0} :
29727                              {top: 2, left: 0, right:0, bottom: 2});
29728         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29729         this.bottomTabs = c.tabPosition != "top";
29730         this.autoScroll = c.autoScroll || false;
29731         if(this.autoScroll){
29732             this.bodyEl.setStyle("overflow", "auto");
29733         }else{
29734             this.bodyEl.setStyle("overflow", "hidden");
29735         }
29736         //if(c.titlebar !== false){
29737             if((!c.titlebar && !c.title) || c.titlebar === false){
29738                 this.titleEl.hide();
29739             }else{
29740                 this.titleEl.show();
29741                 if(c.title){
29742                     this.titleTextEl.innerHTML = c.title;
29743                 }
29744             }
29745         //}
29746         this.duration = c.duration || .30;
29747         this.slideDuration = c.slideDuration || .45;
29748         this.config = c;
29749         if(c.collapsed){
29750             this.collapse(true);
29751         }
29752         if(c.hidden){
29753             this.hide();
29754         }
29755     },
29756     /**
29757      * Returns true if this region is currently visible.
29758      * @return {Boolean}
29759      */
29760     isVisible : function(){
29761         return this.visible;
29762     },
29763
29764     /**
29765      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
29766      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
29767      */
29768     setCollapsedTitle : function(title){
29769         title = title || "&#160;";
29770         if(this.collapsedTitleTextEl){
29771             this.collapsedTitleTextEl.innerHTML = title;
29772         }
29773     },
29774
29775     getBox : function(){
29776         var b;
29777         if(!this.collapsed){
29778             b = this.el.getBox(false, true);
29779         }else{
29780             b = this.collapsedEl.getBox(false, true);
29781         }
29782         return b;
29783     },
29784
29785     getMargins : function(){
29786         return this.collapsed ? this.cmargins : this.margins;
29787     },
29788
29789     highlight : function(){
29790         this.el.addClass("x-layout-panel-dragover");
29791     },
29792
29793     unhighlight : function(){
29794         this.el.removeClass("x-layout-panel-dragover");
29795     },
29796
29797     updateBox : function(box){
29798         this.box = box;
29799         if(!this.collapsed){
29800             this.el.dom.style.left = box.x + "px";
29801             this.el.dom.style.top = box.y + "px";
29802             this.updateBody(box.width, box.height);
29803         }else{
29804             this.collapsedEl.dom.style.left = box.x + "px";
29805             this.collapsedEl.dom.style.top = box.y + "px";
29806             this.collapsedEl.setSize(box.width, box.height);
29807         }
29808         if(this.tabs){
29809             this.tabs.autoSizeTabs();
29810         }
29811     },
29812
29813     updateBody : function(w, h){
29814         if(w !== null){
29815             this.el.setWidth(w);
29816             w -= this.el.getBorderWidth("rl");
29817             if(this.config.adjustments){
29818                 w += this.config.adjustments[0];
29819             }
29820         }
29821         if(h !== null){
29822             this.el.setHeight(h);
29823             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
29824             h -= this.el.getBorderWidth("tb");
29825             if(this.config.adjustments){
29826                 h += this.config.adjustments[1];
29827             }
29828             this.bodyEl.setHeight(h);
29829             if(this.tabs){
29830                 h = this.tabs.syncHeight(h);
29831             }
29832         }
29833         if(this.panelSize){
29834             w = w !== null ? w : this.panelSize.width;
29835             h = h !== null ? h : this.panelSize.height;
29836         }
29837         if(this.activePanel){
29838             var el = this.activePanel.getEl();
29839             w = w !== null ? w : el.getWidth();
29840             h = h !== null ? h : el.getHeight();
29841             this.panelSize = {width: w, height: h};
29842             this.activePanel.setSize(w, h);
29843         }
29844         if(Roo.isIE && this.tabs){
29845             this.tabs.el.repaint();
29846         }
29847     },
29848
29849     /**
29850      * Returns the container element for this region.
29851      * @return {Roo.Element}
29852      */
29853     getEl : function(){
29854         return this.el;
29855     },
29856
29857     /**
29858      * Hides this region.
29859      */
29860     hide : function(){
29861         if(!this.collapsed){
29862             this.el.dom.style.left = "-2000px";
29863             this.el.hide();
29864         }else{
29865             this.collapsedEl.dom.style.left = "-2000px";
29866             this.collapsedEl.hide();
29867         }
29868         this.visible = false;
29869         this.fireEvent("visibilitychange", this, false);
29870     },
29871
29872     /**
29873      * Shows this region if it was previously hidden.
29874      */
29875     show : function(){
29876         if(!this.collapsed){
29877             this.el.show();
29878         }else{
29879             this.collapsedEl.show();
29880         }
29881         this.visible = true;
29882         this.fireEvent("visibilitychange", this, true);
29883     },
29884
29885     closeClicked : function(){
29886         if(this.activePanel){
29887             this.remove(this.activePanel);
29888         }
29889     },
29890
29891     collapseClick : function(e){
29892         if(this.isSlid){
29893            e.stopPropagation();
29894            this.slideIn();
29895         }else{
29896            e.stopPropagation();
29897            this.slideOut();
29898         }
29899     },
29900
29901     /**
29902      * Collapses this region.
29903      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
29904      */
29905     collapse : function(skipAnim, skipCheck = false){
29906         if(this.collapsed) {
29907             return;
29908         }
29909         
29910         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
29911             
29912             this.collapsed = true;
29913             if(this.split){
29914                 this.split.el.hide();
29915             }
29916             if(this.config.animate && skipAnim !== true){
29917                 this.fireEvent("invalidated", this);
29918                 this.animateCollapse();
29919             }else{
29920                 this.el.setLocation(-20000,-20000);
29921                 this.el.hide();
29922                 this.collapsedEl.show();
29923                 this.fireEvent("collapsed", this);
29924                 this.fireEvent("invalidated", this);
29925             }
29926         }
29927         
29928     },
29929
29930     animateCollapse : function(){
29931         // overridden
29932     },
29933
29934     /**
29935      * Expands this region if it was previously collapsed.
29936      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
29937      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
29938      */
29939     expand : function(e, skipAnim){
29940         if(e) {
29941             e.stopPropagation();
29942         }
29943         if(!this.collapsed || this.el.hasActiveFx()) {
29944             return;
29945         }
29946         if(this.isSlid){
29947             this.afterSlideIn();
29948             skipAnim = true;
29949         }
29950         this.collapsed = false;
29951         if(this.config.animate && skipAnim !== true){
29952             this.animateExpand();
29953         }else{
29954             this.el.show();
29955             if(this.split){
29956                 this.split.el.show();
29957             }
29958             this.collapsedEl.setLocation(-2000,-2000);
29959             this.collapsedEl.hide();
29960             this.fireEvent("invalidated", this);
29961             this.fireEvent("expanded", this);
29962         }
29963     },
29964
29965     animateExpand : function(){
29966         // overridden
29967     },
29968
29969     initTabs : function()
29970     {
29971         this.bodyEl.setStyle("overflow", "hidden");
29972         var ts = new Roo.TabPanel(
29973                 this.bodyEl.dom,
29974                 {
29975                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
29976                     disableTooltips: this.config.disableTabTips,
29977                     toolbar : this.config.toolbar
29978                 }
29979         );
29980         if(this.config.hideTabs){
29981             ts.stripWrap.setDisplayed(false);
29982         }
29983         this.tabs = ts;
29984         ts.resizeTabs = this.config.resizeTabs === true;
29985         ts.minTabWidth = this.config.minTabWidth || 40;
29986         ts.maxTabWidth = this.config.maxTabWidth || 250;
29987         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
29988         ts.monitorResize = false;
29989         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
29990         ts.bodyEl.addClass('x-layout-tabs-body');
29991         this.panels.each(this.initPanelAsTab, this);
29992     },
29993
29994     initPanelAsTab : function(panel){
29995         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
29996                     this.config.closeOnTab && panel.isClosable());
29997         if(panel.tabTip !== undefined){
29998             ti.setTooltip(panel.tabTip);
29999         }
30000         ti.on("activate", function(){
30001               this.setActivePanel(panel);
30002         }, this);
30003         if(this.config.closeOnTab){
30004             ti.on("beforeclose", function(t, e){
30005                 e.cancel = true;
30006                 this.remove(panel);
30007             }, this);
30008         }
30009         return ti;
30010     },
30011
30012     updatePanelTitle : function(panel, title){
30013         if(this.activePanel == panel){
30014             this.updateTitle(title);
30015         }
30016         if(this.tabs){
30017             var ti = this.tabs.getTab(panel.getEl().id);
30018             ti.setText(title);
30019             if(panel.tabTip !== undefined){
30020                 ti.setTooltip(panel.tabTip);
30021             }
30022         }
30023     },
30024
30025     updateTitle : function(title){
30026         if(this.titleTextEl && !this.config.title){
30027             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
30028         }
30029     },
30030
30031     setActivePanel : function(panel){
30032         panel = this.getPanel(panel);
30033         if(this.activePanel && this.activePanel != panel){
30034             this.activePanel.setActiveState(false);
30035         }
30036         this.activePanel = panel;
30037         panel.setActiveState(true);
30038         if(this.panelSize){
30039             panel.setSize(this.panelSize.width, this.panelSize.height);
30040         }
30041         if(this.closeBtn){
30042             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
30043         }
30044         this.updateTitle(panel.getTitle());
30045         if(this.tabs){
30046             this.fireEvent("invalidated", this);
30047         }
30048         this.fireEvent("panelactivated", this, panel);
30049     },
30050
30051     /**
30052      * Shows the specified panel.
30053      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
30054      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
30055      */
30056     showPanel : function(panel)
30057     {
30058         panel = this.getPanel(panel);
30059         if(panel){
30060             if(this.tabs){
30061                 var tab = this.tabs.getTab(panel.getEl().id);
30062                 if(tab.isHidden()){
30063                     this.tabs.unhideTab(tab.id);
30064                 }
30065                 tab.activate();
30066             }else{
30067                 this.setActivePanel(panel);
30068             }
30069         }
30070         return panel;
30071     },
30072
30073     /**
30074      * Get the active panel for this region.
30075      * @return {Roo.ContentPanel} The active panel or null
30076      */
30077     getActivePanel : function(){
30078         return this.activePanel;
30079     },
30080
30081     validateVisibility : function(){
30082         if(this.panels.getCount() < 1){
30083             this.updateTitle("&#160;");
30084             this.closeBtn.hide();
30085             this.hide();
30086         }else{
30087             if(!this.isVisible()){
30088                 this.show();
30089             }
30090         }
30091     },
30092
30093     /**
30094      * Adds the passed ContentPanel(s) to this region.
30095      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
30096      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
30097      */
30098     add : function(panel){
30099         if(arguments.length > 1){
30100             for(var i = 0, len = arguments.length; i < len; i++) {
30101                 this.add(arguments[i]);
30102             }
30103             return null;
30104         }
30105         if(this.hasPanel(panel)){
30106             this.showPanel(panel);
30107             return panel;
30108         }
30109         panel.setRegion(this);
30110         this.panels.add(panel);
30111         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
30112             this.bodyEl.dom.appendChild(panel.getEl().dom);
30113             if(panel.background !== true){
30114                 this.setActivePanel(panel);
30115             }
30116             this.fireEvent("paneladded", this, panel);
30117             return panel;
30118         }
30119         if(!this.tabs){
30120             this.initTabs();
30121         }else{
30122             this.initPanelAsTab(panel);
30123         }
30124         if(panel.background !== true){
30125             this.tabs.activate(panel.getEl().id);
30126         }
30127         this.fireEvent("paneladded", this, panel);
30128         return panel;
30129     },
30130
30131     /**
30132      * Hides the tab for the specified panel.
30133      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30134      */
30135     hidePanel : function(panel){
30136         if(this.tabs && (panel = this.getPanel(panel))){
30137             this.tabs.hideTab(panel.getEl().id);
30138         }
30139     },
30140
30141     /**
30142      * Unhides the tab for a previously hidden panel.
30143      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30144      */
30145     unhidePanel : function(panel){
30146         if(this.tabs && (panel = this.getPanel(panel))){
30147             this.tabs.unhideTab(panel.getEl().id);
30148         }
30149     },
30150
30151     clearPanels : function(){
30152         while(this.panels.getCount() > 0){
30153              this.remove(this.panels.first());
30154         }
30155     },
30156
30157     /**
30158      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30159      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30160      * @param {Boolean} preservePanel Overrides the config preservePanel option
30161      * @return {Roo.ContentPanel} The panel that was removed
30162      */
30163     remove : function(panel, preservePanel){
30164         panel = this.getPanel(panel);
30165         if(!panel){
30166             return null;
30167         }
30168         var e = {};
30169         this.fireEvent("beforeremove", this, panel, e);
30170         if(e.cancel === true){
30171             return null;
30172         }
30173         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
30174         var panelId = panel.getId();
30175         this.panels.removeKey(panelId);
30176         if(preservePanel){
30177             document.body.appendChild(panel.getEl().dom);
30178         }
30179         if(this.tabs){
30180             this.tabs.removeTab(panel.getEl().id);
30181         }else if (!preservePanel){
30182             this.bodyEl.dom.removeChild(panel.getEl().dom);
30183         }
30184         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
30185             var p = this.panels.first();
30186             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
30187             tempEl.appendChild(p.getEl().dom);
30188             this.bodyEl.update("");
30189             this.bodyEl.dom.appendChild(p.getEl().dom);
30190             tempEl = null;
30191             this.updateTitle(p.getTitle());
30192             this.tabs = null;
30193             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30194             this.setActivePanel(p);
30195         }
30196         panel.setRegion(null);
30197         if(this.activePanel == panel){
30198             this.activePanel = null;
30199         }
30200         if(this.config.autoDestroy !== false && preservePanel !== true){
30201             try{panel.destroy();}catch(e){}
30202         }
30203         this.fireEvent("panelremoved", this, panel);
30204         return panel;
30205     },
30206
30207     /**
30208      * Returns the TabPanel component used by this region
30209      * @return {Roo.TabPanel}
30210      */
30211     getTabs : function(){
30212         return this.tabs;
30213     },
30214
30215     createTool : function(parentEl, className){
30216         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
30217             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
30218         btn.addClassOnOver("x-layout-tools-button-over");
30219         return btn;
30220     }
30221 });/*
30222  * Based on:
30223  * Ext JS Library 1.1.1
30224  * Copyright(c) 2006-2007, Ext JS, LLC.
30225  *
30226  * Originally Released Under LGPL - original licence link has changed is not relivant.
30227  *
30228  * Fork - LGPL
30229  * <script type="text/javascript">
30230  */
30231  
30232
30233
30234 /**
30235  * @class Roo.SplitLayoutRegion
30236  * @extends Roo.LayoutRegion
30237  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
30238  */
30239 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
30240     this.cursor = cursor;
30241     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
30242 };
30243
30244 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
30245     splitTip : "Drag to resize.",
30246     collapsibleSplitTip : "Drag to resize. Double click to hide.",
30247     useSplitTips : false,
30248
30249     applyConfig : function(config){
30250         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
30251         if(config.split){
30252             if(!this.split){
30253                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
30254                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
30255                 /** The SplitBar for this region 
30256                 * @type Roo.SplitBar */
30257                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
30258                 this.split.on("moved", this.onSplitMove, this);
30259                 this.split.useShim = config.useShim === true;
30260                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
30261                 if(this.useSplitTips){
30262                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
30263                 }
30264                 if(config.collapsible){
30265                     this.split.el.on("dblclick", this.collapse,  this);
30266                 }
30267             }
30268             if(typeof config.minSize != "undefined"){
30269                 this.split.minSize = config.minSize;
30270             }
30271             if(typeof config.maxSize != "undefined"){
30272                 this.split.maxSize = config.maxSize;
30273             }
30274             if(config.hideWhenEmpty || config.hidden || config.collapsed){
30275                 this.hideSplitter();
30276             }
30277         }
30278     },
30279
30280     getHMaxSize : function(){
30281          var cmax = this.config.maxSize || 10000;
30282          var center = this.mgr.getRegion("center");
30283          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
30284     },
30285
30286     getVMaxSize : function(){
30287          var cmax = this.config.maxSize || 10000;
30288          var center = this.mgr.getRegion("center");
30289          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
30290     },
30291
30292     onSplitMove : function(split, newSize){
30293         this.fireEvent("resized", this, newSize);
30294     },
30295     
30296     /** 
30297      * Returns the {@link Roo.SplitBar} for this region.
30298      * @return {Roo.SplitBar}
30299      */
30300     getSplitBar : function(){
30301         return this.split;
30302     },
30303     
30304     hide : function(){
30305         this.hideSplitter();
30306         Roo.SplitLayoutRegion.superclass.hide.call(this);
30307     },
30308
30309     hideSplitter : function(){
30310         if(this.split){
30311             this.split.el.setLocation(-2000,-2000);
30312             this.split.el.hide();
30313         }
30314     },
30315
30316     show : function(){
30317         if(this.split){
30318             this.split.el.show();
30319         }
30320         Roo.SplitLayoutRegion.superclass.show.call(this);
30321     },
30322     
30323     beforeSlide: function(){
30324         if(Roo.isGecko){// firefox overflow auto bug workaround
30325             this.bodyEl.clip();
30326             if(this.tabs) {
30327                 this.tabs.bodyEl.clip();
30328             }
30329             if(this.activePanel){
30330                 this.activePanel.getEl().clip();
30331                 
30332                 if(this.activePanel.beforeSlide){
30333                     this.activePanel.beforeSlide();
30334                 }
30335             }
30336         }
30337     },
30338     
30339     afterSlide : function(){
30340         if(Roo.isGecko){// firefox overflow auto bug workaround
30341             this.bodyEl.unclip();
30342             if(this.tabs) {
30343                 this.tabs.bodyEl.unclip();
30344             }
30345             if(this.activePanel){
30346                 this.activePanel.getEl().unclip();
30347                 if(this.activePanel.afterSlide){
30348                     this.activePanel.afterSlide();
30349                 }
30350             }
30351         }
30352     },
30353
30354     initAutoHide : function(){
30355         if(this.autoHide !== false){
30356             if(!this.autoHideHd){
30357                 var st = new Roo.util.DelayedTask(this.slideIn, this);
30358                 this.autoHideHd = {
30359                     "mouseout": function(e){
30360                         if(!e.within(this.el, true)){
30361                             st.delay(500);
30362                         }
30363                     },
30364                     "mouseover" : function(e){
30365                         st.cancel();
30366                     },
30367                     scope : this
30368                 };
30369             }
30370             this.el.on(this.autoHideHd);
30371         }
30372     },
30373
30374     clearAutoHide : function(){
30375         if(this.autoHide !== false){
30376             this.el.un("mouseout", this.autoHideHd.mouseout);
30377             this.el.un("mouseover", this.autoHideHd.mouseover);
30378         }
30379     },
30380
30381     clearMonitor : function(){
30382         Roo.get(document).un("click", this.slideInIf, this);
30383     },
30384
30385     // these names are backwards but not changed for compat
30386     slideOut : function(){
30387         if(this.isSlid || this.el.hasActiveFx()){
30388             return;
30389         }
30390         this.isSlid = true;
30391         if(this.collapseBtn){
30392             this.collapseBtn.hide();
30393         }
30394         this.closeBtnState = this.closeBtn.getStyle('display');
30395         this.closeBtn.hide();
30396         if(this.stickBtn){
30397             this.stickBtn.show();
30398         }
30399         this.el.show();
30400         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
30401         this.beforeSlide();
30402         this.el.setStyle("z-index", 10001);
30403         this.el.slideIn(this.getSlideAnchor(), {
30404             callback: function(){
30405                 this.afterSlide();
30406                 this.initAutoHide();
30407                 Roo.get(document).on("click", this.slideInIf, this);
30408                 this.fireEvent("slideshow", this);
30409             },
30410             scope: this,
30411             block: true
30412         });
30413     },
30414
30415     afterSlideIn : function(){
30416         this.clearAutoHide();
30417         this.isSlid = false;
30418         this.clearMonitor();
30419         this.el.setStyle("z-index", "");
30420         if(this.collapseBtn){
30421             this.collapseBtn.show();
30422         }
30423         this.closeBtn.setStyle('display', this.closeBtnState);
30424         if(this.stickBtn){
30425             this.stickBtn.hide();
30426         }
30427         this.fireEvent("slidehide", this);
30428     },
30429
30430     slideIn : function(cb){
30431         if(!this.isSlid || this.el.hasActiveFx()){
30432             Roo.callback(cb);
30433             return;
30434         }
30435         this.isSlid = false;
30436         this.beforeSlide();
30437         this.el.slideOut(this.getSlideAnchor(), {
30438             callback: function(){
30439                 this.el.setLeftTop(-10000, -10000);
30440                 this.afterSlide();
30441                 this.afterSlideIn();
30442                 Roo.callback(cb);
30443             },
30444             scope: this,
30445             block: true
30446         });
30447     },
30448     
30449     slideInIf : function(e){
30450         if(!e.within(this.el)){
30451             this.slideIn();
30452         }
30453     },
30454
30455     animateCollapse : function(){
30456         this.beforeSlide();
30457         this.el.setStyle("z-index", 20000);
30458         var anchor = this.getSlideAnchor();
30459         this.el.slideOut(anchor, {
30460             callback : function(){
30461                 this.el.setStyle("z-index", "");
30462                 this.collapsedEl.slideIn(anchor, {duration:.3});
30463                 this.afterSlide();
30464                 this.el.setLocation(-10000,-10000);
30465                 this.el.hide();
30466                 this.fireEvent("collapsed", this);
30467             },
30468             scope: this,
30469             block: true
30470         });
30471     },
30472
30473     animateExpand : function(){
30474         this.beforeSlide();
30475         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
30476         this.el.setStyle("z-index", 20000);
30477         this.collapsedEl.hide({
30478             duration:.1
30479         });
30480         this.el.slideIn(this.getSlideAnchor(), {
30481             callback : function(){
30482                 this.el.setStyle("z-index", "");
30483                 this.afterSlide();
30484                 if(this.split){
30485                     this.split.el.show();
30486                 }
30487                 this.fireEvent("invalidated", this);
30488                 this.fireEvent("expanded", this);
30489             },
30490             scope: this,
30491             block: true
30492         });
30493     },
30494
30495     anchors : {
30496         "west" : "left",
30497         "east" : "right",
30498         "north" : "top",
30499         "south" : "bottom"
30500     },
30501
30502     sanchors : {
30503         "west" : "l",
30504         "east" : "r",
30505         "north" : "t",
30506         "south" : "b"
30507     },
30508
30509     canchors : {
30510         "west" : "tl-tr",
30511         "east" : "tr-tl",
30512         "north" : "tl-bl",
30513         "south" : "bl-tl"
30514     },
30515
30516     getAnchor : function(){
30517         return this.anchors[this.position];
30518     },
30519
30520     getCollapseAnchor : function(){
30521         return this.canchors[this.position];
30522     },
30523
30524     getSlideAnchor : function(){
30525         return this.sanchors[this.position];
30526     },
30527
30528     getAlignAdj : function(){
30529         var cm = this.cmargins;
30530         switch(this.position){
30531             case "west":
30532                 return [0, 0];
30533             break;
30534             case "east":
30535                 return [0, 0];
30536             break;
30537             case "north":
30538                 return [0, 0];
30539             break;
30540             case "south":
30541                 return [0, 0];
30542             break;
30543         }
30544     },
30545
30546     getExpandAdj : function(){
30547         var c = this.collapsedEl, cm = this.cmargins;
30548         switch(this.position){
30549             case "west":
30550                 return [-(cm.right+c.getWidth()+cm.left), 0];
30551             break;
30552             case "east":
30553                 return [cm.right+c.getWidth()+cm.left, 0];
30554             break;
30555             case "north":
30556                 return [0, -(cm.top+cm.bottom+c.getHeight())];
30557             break;
30558             case "south":
30559                 return [0, cm.top+cm.bottom+c.getHeight()];
30560             break;
30561         }
30562     }
30563 });/*
30564  * Based on:
30565  * Ext JS Library 1.1.1
30566  * Copyright(c) 2006-2007, Ext JS, LLC.
30567  *
30568  * Originally Released Under LGPL - original licence link has changed is not relivant.
30569  *
30570  * Fork - LGPL
30571  * <script type="text/javascript">
30572  */
30573 /*
30574  * These classes are private internal classes
30575  */
30576 Roo.CenterLayoutRegion = function(mgr, config){
30577     Roo.LayoutRegion.call(this, mgr, config, "center");
30578     this.visible = true;
30579     this.minWidth = config.minWidth || 20;
30580     this.minHeight = config.minHeight || 20;
30581 };
30582
30583 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
30584     hide : function(){
30585         // center panel can't be hidden
30586     },
30587     
30588     show : function(){
30589         // center panel can't be hidden
30590     },
30591     
30592     getMinWidth: function(){
30593         return this.minWidth;
30594     },
30595     
30596     getMinHeight: function(){
30597         return this.minHeight;
30598     }
30599 });
30600
30601
30602 Roo.NorthLayoutRegion = function(mgr, config){
30603     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
30604     if(this.split){
30605         this.split.placement = Roo.SplitBar.TOP;
30606         this.split.orientation = Roo.SplitBar.VERTICAL;
30607         this.split.el.addClass("x-layout-split-v");
30608     }
30609     var size = config.initialSize || config.height;
30610     if(typeof size != "undefined"){
30611         this.el.setHeight(size);
30612     }
30613 };
30614 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
30615     orientation: Roo.SplitBar.VERTICAL,
30616     getBox : function(){
30617         if(this.collapsed){
30618             return this.collapsedEl.getBox();
30619         }
30620         var box = this.el.getBox();
30621         if(this.split){
30622             box.height += this.split.el.getHeight();
30623         }
30624         return box;
30625     },
30626     
30627     updateBox : function(box){
30628         if(this.split && !this.collapsed){
30629             box.height -= this.split.el.getHeight();
30630             this.split.el.setLeft(box.x);
30631             this.split.el.setTop(box.y+box.height);
30632             this.split.el.setWidth(box.width);
30633         }
30634         if(this.collapsed){
30635             this.updateBody(box.width, null);
30636         }
30637         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30638     }
30639 });
30640
30641 Roo.SouthLayoutRegion = function(mgr, config){
30642     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
30643     if(this.split){
30644         this.split.placement = Roo.SplitBar.BOTTOM;
30645         this.split.orientation = Roo.SplitBar.VERTICAL;
30646         this.split.el.addClass("x-layout-split-v");
30647     }
30648     var size = config.initialSize || config.height;
30649     if(typeof size != "undefined"){
30650         this.el.setHeight(size);
30651     }
30652 };
30653 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
30654     orientation: Roo.SplitBar.VERTICAL,
30655     getBox : function(){
30656         if(this.collapsed){
30657             return this.collapsedEl.getBox();
30658         }
30659         var box = this.el.getBox();
30660         if(this.split){
30661             var sh = this.split.el.getHeight();
30662             box.height += sh;
30663             box.y -= sh;
30664         }
30665         return box;
30666     },
30667     
30668     updateBox : function(box){
30669         if(this.split && !this.collapsed){
30670             var sh = this.split.el.getHeight();
30671             box.height -= sh;
30672             box.y += sh;
30673             this.split.el.setLeft(box.x);
30674             this.split.el.setTop(box.y-sh);
30675             this.split.el.setWidth(box.width);
30676         }
30677         if(this.collapsed){
30678             this.updateBody(box.width, null);
30679         }
30680         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30681     }
30682 });
30683
30684 Roo.EastLayoutRegion = function(mgr, config){
30685     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
30686     if(this.split){
30687         this.split.placement = Roo.SplitBar.RIGHT;
30688         this.split.orientation = Roo.SplitBar.HORIZONTAL;
30689         this.split.el.addClass("x-layout-split-h");
30690     }
30691     var size = config.initialSize || config.width;
30692     if(typeof size != "undefined"){
30693         this.el.setWidth(size);
30694     }
30695 };
30696 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
30697     orientation: Roo.SplitBar.HORIZONTAL,
30698     getBox : function(){
30699         if(this.collapsed){
30700             return this.collapsedEl.getBox();
30701         }
30702         var box = this.el.getBox();
30703         if(this.split){
30704             var sw = this.split.el.getWidth();
30705             box.width += sw;
30706             box.x -= sw;
30707         }
30708         return box;
30709     },
30710
30711     updateBox : function(box){
30712         if(this.split && !this.collapsed){
30713             var sw = this.split.el.getWidth();
30714             box.width -= sw;
30715             this.split.el.setLeft(box.x);
30716             this.split.el.setTop(box.y);
30717             this.split.el.setHeight(box.height);
30718             box.x += sw;
30719         }
30720         if(this.collapsed){
30721             this.updateBody(null, box.height);
30722         }
30723         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30724     }
30725 });
30726
30727 Roo.WestLayoutRegion = function(mgr, config){
30728     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
30729     if(this.split){
30730         this.split.placement = Roo.SplitBar.LEFT;
30731         this.split.orientation = Roo.SplitBar.HORIZONTAL;
30732         this.split.el.addClass("x-layout-split-h");
30733     }
30734     var size = config.initialSize || config.width;
30735     if(typeof size != "undefined"){
30736         this.el.setWidth(size);
30737     }
30738 };
30739 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
30740     orientation: Roo.SplitBar.HORIZONTAL,
30741     getBox : function(){
30742         if(this.collapsed){
30743             return this.collapsedEl.getBox();
30744         }
30745         var box = this.el.getBox();
30746         if(this.split){
30747             box.width += this.split.el.getWidth();
30748         }
30749         return box;
30750     },
30751     
30752     updateBox : function(box){
30753         if(this.split && !this.collapsed){
30754             var sw = this.split.el.getWidth();
30755             box.width -= sw;
30756             this.split.el.setLeft(box.x+box.width);
30757             this.split.el.setTop(box.y);
30758             this.split.el.setHeight(box.height);
30759         }
30760         if(this.collapsed){
30761             this.updateBody(null, box.height);
30762         }
30763         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30764     }
30765 });
30766 /*
30767  * Based on:
30768  * Ext JS Library 1.1.1
30769  * Copyright(c) 2006-2007, Ext JS, LLC.
30770  *
30771  * Originally Released Under LGPL - original licence link has changed is not relivant.
30772  *
30773  * Fork - LGPL
30774  * <script type="text/javascript">
30775  */
30776  
30777  
30778 /*
30779  * Private internal class for reading and applying state
30780  */
30781 Roo.LayoutStateManager = function(layout){
30782      // default empty state
30783      this.state = {
30784         north: {},
30785         south: {},
30786         east: {},
30787         west: {}       
30788     };
30789 };
30790
30791 Roo.LayoutStateManager.prototype = {
30792     init : function(layout, provider){
30793         this.provider = provider;
30794         var state = provider.get(layout.id+"-layout-state");
30795         if(state){
30796             var wasUpdating = layout.isUpdating();
30797             if(!wasUpdating){
30798                 layout.beginUpdate();
30799             }
30800             for(var key in state){
30801                 if(typeof state[key] != "function"){
30802                     var rstate = state[key];
30803                     var r = layout.getRegion(key);
30804                     if(r && rstate){
30805                         if(rstate.size){
30806                             r.resizeTo(rstate.size);
30807                         }
30808                         if(rstate.collapsed == true){
30809                             r.collapse(true);
30810                         }else{
30811                             r.expand(null, true);
30812                         }
30813                     }
30814                 }
30815             }
30816             if(!wasUpdating){
30817                 layout.endUpdate();
30818             }
30819             this.state = state; 
30820         }
30821         this.layout = layout;
30822         layout.on("regionresized", this.onRegionResized, this);
30823         layout.on("regioncollapsed", this.onRegionCollapsed, this);
30824         layout.on("regionexpanded", this.onRegionExpanded, this);
30825     },
30826     
30827     storeState : function(){
30828         this.provider.set(this.layout.id+"-layout-state", this.state);
30829     },
30830     
30831     onRegionResized : function(region, newSize){
30832         this.state[region.getPosition()].size = newSize;
30833         this.storeState();
30834     },
30835     
30836     onRegionCollapsed : function(region){
30837         this.state[region.getPosition()].collapsed = true;
30838         this.storeState();
30839     },
30840     
30841     onRegionExpanded : function(region){
30842         this.state[region.getPosition()].collapsed = false;
30843         this.storeState();
30844     }
30845 };/*
30846  * Based on:
30847  * Ext JS Library 1.1.1
30848  * Copyright(c) 2006-2007, Ext JS, LLC.
30849  *
30850  * Originally Released Under LGPL - original licence link has changed is not relivant.
30851  *
30852  * Fork - LGPL
30853  * <script type="text/javascript">
30854  */
30855 /**
30856  * @class Roo.ContentPanel
30857  * @extends Roo.util.Observable
30858  * A basic ContentPanel element.
30859  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
30860  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
30861  * @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
30862  * @cfg {Boolean}   closable      True if the panel can be closed/removed
30863  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
30864  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
30865  * @cfg {Toolbar}   toolbar       A toolbar for this panel
30866  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
30867  * @cfg {String} title          The title for this panel
30868  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
30869  * @cfg {String} url            Calls {@link #setUrl} with this value
30870  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
30871  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
30872  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
30873  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
30874
30875  * @constructor
30876  * Create a new ContentPanel.
30877  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
30878  * @param {String/Object} config A string to set only the title or a config object
30879  * @param {String} content (optional) Set the HTML content for this panel
30880  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
30881  */
30882 Roo.ContentPanel = function(el, config, content){
30883     
30884      
30885     /*
30886     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
30887         config = el;
30888         el = Roo.id();
30889     }
30890     if (config && config.parentLayout) { 
30891         el = config.parentLayout.el.createChild(); 
30892     }
30893     */
30894     if(el.autoCreate){ // xtype is available if this is called from factory
30895         config = el;
30896         el = Roo.id();
30897     }
30898     this.el = Roo.get(el);
30899     if(!this.el && config && config.autoCreate){
30900         if(typeof config.autoCreate == "object"){
30901             if(!config.autoCreate.id){
30902                 config.autoCreate.id = config.id||el;
30903             }
30904             this.el = Roo.DomHelper.append(document.body,
30905                         config.autoCreate, true);
30906         }else{
30907             this.el = Roo.DomHelper.append(document.body,
30908                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
30909         }
30910     }
30911     this.closable = false;
30912     this.loaded = false;
30913     this.active = false;
30914     if(typeof config == "string"){
30915         this.title = config;
30916     }else{
30917         Roo.apply(this, config);
30918     }
30919     
30920     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
30921         this.wrapEl = this.el.wrap();
30922         this.toolbar.container = this.el.insertSibling(false, 'before');
30923         this.toolbar = new Roo.Toolbar(this.toolbar);
30924     }
30925     
30926     // xtype created footer. - not sure if will work as we normally have to render first..
30927     if (this.footer && !this.footer.el && this.footer.xtype) {
30928         if (!this.wrapEl) {
30929             this.wrapEl = this.el.wrap();
30930         }
30931     
30932         this.footer.container = this.wrapEl.createChild();
30933          
30934         this.footer = Roo.factory(this.footer, Roo);
30935         
30936     }
30937     
30938     if(this.resizeEl){
30939         this.resizeEl = Roo.get(this.resizeEl, true);
30940     }else{
30941         this.resizeEl = this.el;
30942     }
30943     // handle view.xtype
30944     
30945  
30946     
30947     
30948     this.addEvents({
30949         /**
30950          * @event activate
30951          * Fires when this panel is activated. 
30952          * @param {Roo.ContentPanel} this
30953          */
30954         "activate" : true,
30955         /**
30956          * @event deactivate
30957          * Fires when this panel is activated. 
30958          * @param {Roo.ContentPanel} this
30959          */
30960         "deactivate" : true,
30961
30962         /**
30963          * @event resize
30964          * Fires when this panel is resized if fitToFrame is true.
30965          * @param {Roo.ContentPanel} this
30966          * @param {Number} width The width after any component adjustments
30967          * @param {Number} height The height after any component adjustments
30968          */
30969         "resize" : true,
30970         
30971          /**
30972          * @event render
30973          * Fires when this tab is created
30974          * @param {Roo.ContentPanel} this
30975          */
30976         "render" : true
30977          
30978         
30979     });
30980     
30981
30982     
30983     
30984     if(this.autoScroll){
30985         this.resizeEl.setStyle("overflow", "auto");
30986     } else {
30987         // fix randome scrolling
30988         this.el.on('scroll', function() {
30989             Roo.log('fix random scolling');
30990             this.scrollTo('top',0); 
30991         });
30992     }
30993     content = content || this.content;
30994     if(content){
30995         this.setContent(content);
30996     }
30997     if(config && config.url){
30998         this.setUrl(this.url, this.params, this.loadOnce);
30999     }
31000     
31001     
31002     
31003     Roo.ContentPanel.superclass.constructor.call(this);
31004     
31005     if (this.view && typeof(this.view.xtype) != 'undefined') {
31006         this.view.el = this.el.appendChild(document.createElement("div"));
31007         this.view = Roo.factory(this.view); 
31008         this.view.render  &&  this.view.render(false, '');  
31009     }
31010     
31011     
31012     this.fireEvent('render', this);
31013 };
31014
31015 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
31016     tabTip:'',
31017     setRegion : function(region){
31018         this.region = region;
31019         if(region){
31020            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
31021         }else{
31022            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
31023         } 
31024     },
31025     
31026     /**
31027      * Returns the toolbar for this Panel if one was configured. 
31028      * @return {Roo.Toolbar} 
31029      */
31030     getToolbar : function(){
31031         return this.toolbar;
31032     },
31033     
31034     setActiveState : function(active){
31035         this.active = active;
31036         if(!active){
31037             this.fireEvent("deactivate", this);
31038         }else{
31039             this.fireEvent("activate", this);
31040         }
31041     },
31042     /**
31043      * Updates this panel's element
31044      * @param {String} content The new content
31045      * @param {Boolean} loadScripts (optional) true to look for and process scripts
31046     */
31047     setContent : function(content, loadScripts){
31048         this.el.update(content, loadScripts);
31049     },
31050
31051     ignoreResize : function(w, h){
31052         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
31053             return true;
31054         }else{
31055             this.lastSize = {width: w, height: h};
31056             return false;
31057         }
31058     },
31059     /**
31060      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
31061      * @return {Roo.UpdateManager} The UpdateManager
31062      */
31063     getUpdateManager : function(){
31064         return this.el.getUpdateManager();
31065     },
31066      /**
31067      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
31068      * @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:
31069 <pre><code>
31070 panel.load({
31071     url: "your-url.php",
31072     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
31073     callback: yourFunction,
31074     scope: yourObject, //(optional scope)
31075     discardUrl: false,
31076     nocache: false,
31077     text: "Loading...",
31078     timeout: 30,
31079     scripts: false
31080 });
31081 </code></pre>
31082      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
31083      * 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.
31084      * @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}
31085      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
31086      * @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.
31087      * @return {Roo.ContentPanel} this
31088      */
31089     load : function(){
31090         var um = this.el.getUpdateManager();
31091         um.update.apply(um, arguments);
31092         return this;
31093     },
31094
31095
31096     /**
31097      * 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.
31098      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
31099      * @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)
31100      * @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)
31101      * @return {Roo.UpdateManager} The UpdateManager
31102      */
31103     setUrl : function(url, params, loadOnce){
31104         if(this.refreshDelegate){
31105             this.removeListener("activate", this.refreshDelegate);
31106         }
31107         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
31108         this.on("activate", this.refreshDelegate);
31109         return this.el.getUpdateManager();
31110     },
31111     
31112     _handleRefresh : function(url, params, loadOnce){
31113         if(!loadOnce || !this.loaded){
31114             var updater = this.el.getUpdateManager();
31115             updater.update(url, params, this._setLoaded.createDelegate(this));
31116         }
31117     },
31118     
31119     _setLoaded : function(){
31120         this.loaded = true;
31121     }, 
31122     
31123     /**
31124      * Returns this panel's id
31125      * @return {String} 
31126      */
31127     getId : function(){
31128         return this.el.id;
31129     },
31130     
31131     /** 
31132      * Returns this panel's element - used by regiosn to add.
31133      * @return {Roo.Element} 
31134      */
31135     getEl : function(){
31136         return this.wrapEl || this.el;
31137     },
31138     
31139     adjustForComponents : function(width, height)
31140     {
31141         //Roo.log('adjustForComponents ');
31142         if(this.resizeEl != this.el){
31143             width -= this.el.getFrameWidth('lr');
31144             height -= this.el.getFrameWidth('tb');
31145         }
31146         if(this.toolbar){
31147             var te = this.toolbar.getEl();
31148             height -= te.getHeight();
31149             te.setWidth(width);
31150         }
31151         if(this.footer){
31152             var te = this.footer.getEl();
31153             //Roo.log("footer:" + te.getHeight());
31154             
31155             height -= te.getHeight();
31156             te.setWidth(width);
31157         }
31158         
31159         
31160         if(this.adjustments){
31161             width += this.adjustments[0];
31162             height += this.adjustments[1];
31163         }
31164         return {"width": width, "height": height};
31165     },
31166     
31167     setSize : function(width, height){
31168         if(this.fitToFrame && !this.ignoreResize(width, height)){
31169             if(this.fitContainer && this.resizeEl != this.el){
31170                 this.el.setSize(width, height);
31171             }
31172             var size = this.adjustForComponents(width, height);
31173             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
31174             this.fireEvent('resize', this, size.width, size.height);
31175         }
31176     },
31177     
31178     /**
31179      * Returns this panel's title
31180      * @return {String} 
31181      */
31182     getTitle : function(){
31183         return this.title;
31184     },
31185     
31186     /**
31187      * Set this panel's title
31188      * @param {String} title
31189      */
31190     setTitle : function(title){
31191         this.title = title;
31192         if(this.region){
31193             this.region.updatePanelTitle(this, title);
31194         }
31195     },
31196     
31197     /**
31198      * Returns true is this panel was configured to be closable
31199      * @return {Boolean} 
31200      */
31201     isClosable : function(){
31202         return this.closable;
31203     },
31204     
31205     beforeSlide : function(){
31206         this.el.clip();
31207         this.resizeEl.clip();
31208     },
31209     
31210     afterSlide : function(){
31211         this.el.unclip();
31212         this.resizeEl.unclip();
31213     },
31214     
31215     /**
31216      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
31217      *   Will fail silently if the {@link #setUrl} method has not been called.
31218      *   This does not activate the panel, just updates its content.
31219      */
31220     refresh : function(){
31221         if(this.refreshDelegate){
31222            this.loaded = false;
31223            this.refreshDelegate();
31224         }
31225     },
31226     
31227     /**
31228      * Destroys this panel
31229      */
31230     destroy : function(){
31231         this.el.removeAllListeners();
31232         var tempEl = document.createElement("span");
31233         tempEl.appendChild(this.el.dom);
31234         tempEl.innerHTML = "";
31235         this.el.remove();
31236         this.el = null;
31237     },
31238     
31239     /**
31240      * form - if the content panel contains a form - this is a reference to it.
31241      * @type {Roo.form.Form}
31242      */
31243     form : false,
31244     /**
31245      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
31246      *    This contains a reference to it.
31247      * @type {Roo.View}
31248      */
31249     view : false,
31250     
31251       /**
31252      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
31253      * <pre><code>
31254
31255 layout.addxtype({
31256        xtype : 'Form',
31257        items: [ .... ]
31258    }
31259 );
31260
31261 </code></pre>
31262      * @param {Object} cfg Xtype definition of item to add.
31263      */
31264     
31265     addxtype : function(cfg) {
31266         // add form..
31267         if (cfg.xtype.match(/^Form$/)) {
31268             
31269             var el;
31270             //if (this.footer) {
31271             //    el = this.footer.container.insertSibling(false, 'before');
31272             //} else {
31273                 el = this.el.createChild();
31274             //}
31275
31276             this.form = new  Roo.form.Form(cfg);
31277             
31278             
31279             if ( this.form.allItems.length) {
31280                 this.form.render(el.dom);
31281             }
31282             return this.form;
31283         }
31284         // should only have one of theses..
31285         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
31286             // views.. should not be just added - used named prop 'view''
31287             
31288             cfg.el = this.el.appendChild(document.createElement("div"));
31289             // factory?
31290             
31291             var ret = new Roo.factory(cfg);
31292              
31293              ret.render && ret.render(false, ''); // render blank..
31294             this.view = ret;
31295             return ret;
31296         }
31297         return false;
31298     }
31299 });
31300
31301 /**
31302  * @class Roo.GridPanel
31303  * @extends Roo.ContentPanel
31304  * @constructor
31305  * Create a new GridPanel.
31306  * @param {Roo.grid.Grid} grid The grid for this panel
31307  * @param {String/Object} config A string to set only the panel's title, or a config object
31308  */
31309 Roo.GridPanel = function(grid, config){
31310     
31311   
31312     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
31313         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
31314         
31315     this.wrapper.dom.appendChild(grid.getGridEl().dom);
31316     
31317     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
31318     
31319     if(this.toolbar){
31320         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
31321     }
31322     // xtype created footer. - not sure if will work as we normally have to render first..
31323     if (this.footer && !this.footer.el && this.footer.xtype) {
31324         
31325         this.footer.container = this.grid.getView().getFooterPanel(true);
31326         this.footer.dataSource = this.grid.dataSource;
31327         this.footer = Roo.factory(this.footer, Roo);
31328         
31329     }
31330     
31331     grid.monitorWindowResize = false; // turn off autosizing
31332     grid.autoHeight = false;
31333     grid.autoWidth = false;
31334     this.grid = grid;
31335     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
31336 };
31337
31338 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
31339     getId : function(){
31340         return this.grid.id;
31341     },
31342     
31343     /**
31344      * Returns the grid for this panel
31345      * @return {Roo.grid.Grid} 
31346      */
31347     getGrid : function(){
31348         return this.grid;    
31349     },
31350     
31351     setSize : function(width, height){
31352         if(!this.ignoreResize(width, height)){
31353             var grid = this.grid;
31354             var size = this.adjustForComponents(width, height);
31355             grid.getGridEl().setSize(size.width, size.height);
31356             grid.autoSize();
31357         }
31358     },
31359     
31360     beforeSlide : function(){
31361         this.grid.getView().scroller.clip();
31362     },
31363     
31364     afterSlide : function(){
31365         this.grid.getView().scroller.unclip();
31366     },
31367     
31368     destroy : function(){
31369         this.grid.destroy();
31370         delete this.grid;
31371         Roo.GridPanel.superclass.destroy.call(this); 
31372     }
31373 });
31374
31375
31376 /**
31377  * @class Roo.NestedLayoutPanel
31378  * @extends Roo.ContentPanel
31379  * @constructor
31380  * Create a new NestedLayoutPanel.
31381  * 
31382  * 
31383  * @param {Roo.BorderLayout} layout The layout for this panel
31384  * @param {String/Object} config A string to set only the title or a config object
31385  */
31386 Roo.NestedLayoutPanel = function(layout, config)
31387 {
31388     // construct with only one argument..
31389     /* FIXME - implement nicer consturctors
31390     if (layout.layout) {
31391         config = layout;
31392         layout = config.layout;
31393         delete config.layout;
31394     }
31395     if (layout.xtype && !layout.getEl) {
31396         // then layout needs constructing..
31397         layout = Roo.factory(layout, Roo);
31398     }
31399     */
31400     
31401     
31402     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
31403     
31404     layout.monitorWindowResize = false; // turn off autosizing
31405     this.layout = layout;
31406     this.layout.getEl().addClass("x-layout-nested-layout");
31407     
31408     
31409     
31410     
31411 };
31412
31413 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
31414
31415     setSize : function(width, height){
31416         if(!this.ignoreResize(width, height)){
31417             var size = this.adjustForComponents(width, height);
31418             var el = this.layout.getEl();
31419             el.setSize(size.width, size.height);
31420             var touch = el.dom.offsetWidth;
31421             this.layout.layout();
31422             // ie requires a double layout on the first pass
31423             if(Roo.isIE && !this.initialized){
31424                 this.initialized = true;
31425                 this.layout.layout();
31426             }
31427         }
31428     },
31429     
31430     // activate all subpanels if not currently active..
31431     
31432     setActiveState : function(active){
31433         this.active = active;
31434         if(!active){
31435             this.fireEvent("deactivate", this);
31436             return;
31437         }
31438         
31439         this.fireEvent("activate", this);
31440         // not sure if this should happen before or after..
31441         if (!this.layout) {
31442             return; // should not happen..
31443         }
31444         var reg = false;
31445         for (var r in this.layout.regions) {
31446             reg = this.layout.getRegion(r);
31447             if (reg.getActivePanel()) {
31448                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
31449                 reg.setActivePanel(reg.getActivePanel());
31450                 continue;
31451             }
31452             if (!reg.panels.length) {
31453                 continue;
31454             }
31455             reg.showPanel(reg.getPanel(0));
31456         }
31457         
31458         
31459         
31460         
31461     },
31462     
31463     /**
31464      * Returns the nested BorderLayout for this panel
31465      * @return {Roo.BorderLayout} 
31466      */
31467     getLayout : function(){
31468         return this.layout;
31469     },
31470     
31471      /**
31472      * Adds a xtype elements to the layout of the nested panel
31473      * <pre><code>
31474
31475 panel.addxtype({
31476        xtype : 'ContentPanel',
31477        region: 'west',
31478        items: [ .... ]
31479    }
31480 );
31481
31482 panel.addxtype({
31483         xtype : 'NestedLayoutPanel',
31484         region: 'west',
31485         layout: {
31486            center: { },
31487            west: { }   
31488         },
31489         items : [ ... list of content panels or nested layout panels.. ]
31490    }
31491 );
31492 </code></pre>
31493      * @param {Object} cfg Xtype definition of item to add.
31494      */
31495     addxtype : function(cfg) {
31496         return this.layout.addxtype(cfg);
31497     
31498     }
31499 });
31500
31501 Roo.ScrollPanel = function(el, config, content){
31502     config = config || {};
31503     config.fitToFrame = true;
31504     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
31505     
31506     this.el.dom.style.overflow = "hidden";
31507     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
31508     this.el.removeClass("x-layout-inactive-content");
31509     this.el.on("mousewheel", this.onWheel, this);
31510
31511     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
31512     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
31513     up.unselectable(); down.unselectable();
31514     up.on("click", this.scrollUp, this);
31515     down.on("click", this.scrollDown, this);
31516     up.addClassOnOver("x-scroller-btn-over");
31517     down.addClassOnOver("x-scroller-btn-over");
31518     up.addClassOnClick("x-scroller-btn-click");
31519     down.addClassOnClick("x-scroller-btn-click");
31520     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
31521
31522     this.resizeEl = this.el;
31523     this.el = wrap; this.up = up; this.down = down;
31524 };
31525
31526 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
31527     increment : 100,
31528     wheelIncrement : 5,
31529     scrollUp : function(){
31530         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
31531     },
31532
31533     scrollDown : function(){
31534         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
31535     },
31536
31537     afterScroll : function(){
31538         var el = this.resizeEl;
31539         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
31540         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31541         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31542     },
31543
31544     setSize : function(){
31545         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
31546         this.afterScroll();
31547     },
31548
31549     onWheel : function(e){
31550         var d = e.getWheelDelta();
31551         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
31552         this.afterScroll();
31553         e.stopEvent();
31554     },
31555
31556     setContent : function(content, loadScripts){
31557         this.resizeEl.update(content, loadScripts);
31558     }
31559
31560 });
31561
31562
31563
31564
31565
31566
31567
31568
31569
31570 /**
31571  * @class Roo.TreePanel
31572  * @extends Roo.ContentPanel
31573  * @constructor
31574  * Create a new TreePanel. - defaults to fit/scoll contents.
31575  * @param {String/Object} config A string to set only the panel's title, or a config object
31576  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
31577  */
31578 Roo.TreePanel = function(config){
31579     var el = config.el;
31580     var tree = config.tree;
31581     delete config.tree; 
31582     delete config.el; // hopefull!
31583     
31584     // wrapper for IE7 strict & safari scroll issue
31585     
31586     var treeEl = el.createChild();
31587     config.resizeEl = treeEl;
31588     
31589     
31590     
31591     Roo.TreePanel.superclass.constructor.call(this, el, config);
31592  
31593  
31594     this.tree = new Roo.tree.TreePanel(treeEl , tree);
31595     //console.log(tree);
31596     this.on('activate', function()
31597     {
31598         if (this.tree.rendered) {
31599             return;
31600         }
31601         //console.log('render tree');
31602         this.tree.render();
31603     });
31604     // this should not be needed.. - it's actually the 'el' that resizes?
31605     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
31606     
31607     //this.on('resize',  function (cp, w, h) {
31608     //        this.tree.innerCt.setWidth(w);
31609     //        this.tree.innerCt.setHeight(h);
31610     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
31611     //});
31612
31613         
31614     
31615 };
31616
31617 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
31618     fitToFrame : true,
31619     autoScroll : true
31620 });
31621
31622
31623
31624
31625
31626
31627
31628
31629
31630
31631
31632 /*
31633  * Based on:
31634  * Ext JS Library 1.1.1
31635  * Copyright(c) 2006-2007, Ext JS, LLC.
31636  *
31637  * Originally Released Under LGPL - original licence link has changed is not relivant.
31638  *
31639  * Fork - LGPL
31640  * <script type="text/javascript">
31641  */
31642  
31643
31644 /**
31645  * @class Roo.ReaderLayout
31646  * @extends Roo.BorderLayout
31647  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
31648  * center region containing two nested regions (a top one for a list view and one for item preview below),
31649  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
31650  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
31651  * expedites the setup of the overall layout and regions for this common application style.
31652  * Example:
31653  <pre><code>
31654 var reader = new Roo.ReaderLayout();
31655 var CP = Roo.ContentPanel;  // shortcut for adding
31656
31657 reader.beginUpdate();
31658 reader.add("north", new CP("north", "North"));
31659 reader.add("west", new CP("west", {title: "West"}));
31660 reader.add("east", new CP("east", {title: "East"}));
31661
31662 reader.regions.listView.add(new CP("listView", "List"));
31663 reader.regions.preview.add(new CP("preview", "Preview"));
31664 reader.endUpdate();
31665 </code></pre>
31666 * @constructor
31667 * Create a new ReaderLayout
31668 * @param {Object} config Configuration options
31669 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
31670 * document.body if omitted)
31671 */
31672 Roo.ReaderLayout = function(config, renderTo){
31673     var c = config || {size:{}};
31674     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
31675         north: c.north !== false ? Roo.apply({
31676             split:false,
31677             initialSize: 32,
31678             titlebar: false
31679         }, c.north) : false,
31680         west: c.west !== false ? Roo.apply({
31681             split:true,
31682             initialSize: 200,
31683             minSize: 175,
31684             maxSize: 400,
31685             titlebar: true,
31686             collapsible: true,
31687             animate: true,
31688             margins:{left:5,right:0,bottom:5,top:5},
31689             cmargins:{left:5,right:5,bottom:5,top:5}
31690         }, c.west) : false,
31691         east: c.east !== false ? Roo.apply({
31692             split:true,
31693             initialSize: 200,
31694             minSize: 175,
31695             maxSize: 400,
31696             titlebar: true,
31697             collapsible: true,
31698             animate: true,
31699             margins:{left:0,right:5,bottom:5,top:5},
31700             cmargins:{left:5,right:5,bottom:5,top:5}
31701         }, c.east) : false,
31702         center: Roo.apply({
31703             tabPosition: 'top',
31704             autoScroll:false,
31705             closeOnTab: true,
31706             titlebar:false,
31707             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
31708         }, c.center)
31709     });
31710
31711     this.el.addClass('x-reader');
31712
31713     this.beginUpdate();
31714
31715     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
31716         south: c.preview !== false ? Roo.apply({
31717             split:true,
31718             initialSize: 200,
31719             minSize: 100,
31720             autoScroll:true,
31721             collapsible:true,
31722             titlebar: true,
31723             cmargins:{top:5,left:0, right:0, bottom:0}
31724         }, c.preview) : false,
31725         center: Roo.apply({
31726             autoScroll:false,
31727             titlebar:false,
31728             minHeight:200
31729         }, c.listView)
31730     });
31731     this.add('center', new Roo.NestedLayoutPanel(inner,
31732             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
31733
31734     this.endUpdate();
31735
31736     this.regions.preview = inner.getRegion('south');
31737     this.regions.listView = inner.getRegion('center');
31738 };
31739
31740 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
31741  * Based on:
31742  * Ext JS Library 1.1.1
31743  * Copyright(c) 2006-2007, Ext JS, LLC.
31744  *
31745  * Originally Released Under LGPL - original licence link has changed is not relivant.
31746  *
31747  * Fork - LGPL
31748  * <script type="text/javascript">
31749  */
31750  
31751 /**
31752  * @class Roo.grid.Grid
31753  * @extends Roo.util.Observable
31754  * This class represents the primary interface of a component based grid control.
31755  * <br><br>Usage:<pre><code>
31756  var grid = new Roo.grid.Grid("my-container-id", {
31757      ds: myDataStore,
31758      cm: myColModel,
31759      selModel: mySelectionModel,
31760      autoSizeColumns: true,
31761      monitorWindowResize: false,
31762      trackMouseOver: true
31763  });
31764  // set any options
31765  grid.render();
31766  * </code></pre>
31767  * <b>Common Problems:</b><br/>
31768  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
31769  * element will correct this<br/>
31770  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
31771  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
31772  * are unpredictable.<br/>
31773  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
31774  * grid to calculate dimensions/offsets.<br/>
31775   * @constructor
31776  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
31777  * The container MUST have some type of size defined for the grid to fill. The container will be
31778  * automatically set to position relative if it isn't already.
31779  * @param {Object} config A config object that sets properties on this grid.
31780  */
31781 Roo.grid.Grid = function(container, config){
31782         // initialize the container
31783         this.container = Roo.get(container);
31784         this.container.update("");
31785         this.container.setStyle("overflow", "hidden");
31786     this.container.addClass('x-grid-container');
31787
31788     this.id = this.container.id;
31789
31790     Roo.apply(this, config);
31791     // check and correct shorthanded configs
31792     if(this.ds){
31793         this.dataSource = this.ds;
31794         delete this.ds;
31795     }
31796     if(this.cm){
31797         this.colModel = this.cm;
31798         delete this.cm;
31799     }
31800     if(this.sm){
31801         this.selModel = this.sm;
31802         delete this.sm;
31803     }
31804
31805     if (this.selModel) {
31806         this.selModel = Roo.factory(this.selModel, Roo.grid);
31807         this.sm = this.selModel;
31808         this.sm.xmodule = this.xmodule || false;
31809     }
31810     if (typeof(this.colModel.config) == 'undefined') {
31811         this.colModel = new Roo.grid.ColumnModel(this.colModel);
31812         this.cm = this.colModel;
31813         this.cm.xmodule = this.xmodule || false;
31814     }
31815     if (this.dataSource) {
31816         this.dataSource= Roo.factory(this.dataSource, Roo.data);
31817         this.ds = this.dataSource;
31818         this.ds.xmodule = this.xmodule || false;
31819          
31820     }
31821     
31822     
31823     
31824     if(this.width){
31825         this.container.setWidth(this.width);
31826     }
31827
31828     if(this.height){
31829         this.container.setHeight(this.height);
31830     }
31831     /** @private */
31832         this.addEvents({
31833         // raw events
31834         /**
31835          * @event click
31836          * The raw click event for the entire grid.
31837          * @param {Roo.EventObject} e
31838          */
31839         "click" : true,
31840         /**
31841          * @event dblclick
31842          * The raw dblclick event for the entire grid.
31843          * @param {Roo.EventObject} e
31844          */
31845         "dblclick" : true,
31846         /**
31847          * @event contextmenu
31848          * The raw contextmenu event for the entire grid.
31849          * @param {Roo.EventObject} e
31850          */
31851         "contextmenu" : true,
31852         /**
31853          * @event mousedown
31854          * The raw mousedown event for the entire grid.
31855          * @param {Roo.EventObject} e
31856          */
31857         "mousedown" : true,
31858         /**
31859          * @event mouseup
31860          * The raw mouseup event for the entire grid.
31861          * @param {Roo.EventObject} e
31862          */
31863         "mouseup" : true,
31864         /**
31865          * @event mouseover
31866          * The raw mouseover event for the entire grid.
31867          * @param {Roo.EventObject} e
31868          */
31869         "mouseover" : true,
31870         /**
31871          * @event mouseout
31872          * The raw mouseout event for the entire grid.
31873          * @param {Roo.EventObject} e
31874          */
31875         "mouseout" : true,
31876         /**
31877          * @event keypress
31878          * The raw keypress event for the entire grid.
31879          * @param {Roo.EventObject} e
31880          */
31881         "keypress" : true,
31882         /**
31883          * @event keydown
31884          * The raw keydown event for the entire grid.
31885          * @param {Roo.EventObject} e
31886          */
31887         "keydown" : true,
31888
31889         // custom events
31890
31891         /**
31892          * @event cellclick
31893          * Fires when a cell is clicked
31894          * @param {Grid} this
31895          * @param {Number} rowIndex
31896          * @param {Number} columnIndex
31897          * @param {Roo.EventObject} e
31898          */
31899         "cellclick" : true,
31900         /**
31901          * @event celldblclick
31902          * Fires when a cell is double clicked
31903          * @param {Grid} this
31904          * @param {Number} rowIndex
31905          * @param {Number} columnIndex
31906          * @param {Roo.EventObject} e
31907          */
31908         "celldblclick" : true,
31909         /**
31910          * @event rowclick
31911          * Fires when a row is clicked
31912          * @param {Grid} this
31913          * @param {Number} rowIndex
31914          * @param {Roo.EventObject} e
31915          */
31916         "rowclick" : true,
31917         /**
31918          * @event rowdblclick
31919          * Fires when a row is double clicked
31920          * @param {Grid} this
31921          * @param {Number} rowIndex
31922          * @param {Roo.EventObject} e
31923          */
31924         "rowdblclick" : true,
31925         /**
31926          * @event headerclick
31927          * Fires when a header is clicked
31928          * @param {Grid} this
31929          * @param {Number} columnIndex
31930          * @param {Roo.EventObject} e
31931          */
31932         "headerclick" : true,
31933         /**
31934          * @event headerdblclick
31935          * Fires when a header cell is double clicked
31936          * @param {Grid} this
31937          * @param {Number} columnIndex
31938          * @param {Roo.EventObject} e
31939          */
31940         "headerdblclick" : true,
31941         /**
31942          * @event rowcontextmenu
31943          * Fires when a row is right clicked
31944          * @param {Grid} this
31945          * @param {Number} rowIndex
31946          * @param {Roo.EventObject} e
31947          */
31948         "rowcontextmenu" : true,
31949         /**
31950          * @event cellcontextmenu
31951          * Fires when a cell is right clicked
31952          * @param {Grid} this
31953          * @param {Number} rowIndex
31954          * @param {Number} cellIndex
31955          * @param {Roo.EventObject} e
31956          */
31957          "cellcontextmenu" : true,
31958         /**
31959          * @event headercontextmenu
31960          * Fires when a header is right clicked
31961          * @param {Grid} this
31962          * @param {Number} columnIndex
31963          * @param {Roo.EventObject} e
31964          */
31965         "headercontextmenu" : true,
31966         /**
31967          * @event bodyscroll
31968          * Fires when the body element is scrolled
31969          * @param {Number} scrollLeft
31970          * @param {Number} scrollTop
31971          */
31972         "bodyscroll" : true,
31973         /**
31974          * @event columnresize
31975          * Fires when the user resizes a column
31976          * @param {Number} columnIndex
31977          * @param {Number} newSize
31978          */
31979         "columnresize" : true,
31980         /**
31981          * @event columnmove
31982          * Fires when the user moves a column
31983          * @param {Number} oldIndex
31984          * @param {Number} newIndex
31985          */
31986         "columnmove" : true,
31987         /**
31988          * @event startdrag
31989          * Fires when row(s) start being dragged
31990          * @param {Grid} this
31991          * @param {Roo.GridDD} dd The drag drop object
31992          * @param {event} e The raw browser event
31993          */
31994         "startdrag" : true,
31995         /**
31996          * @event enddrag
31997          * Fires when a drag operation is complete
31998          * @param {Grid} this
31999          * @param {Roo.GridDD} dd The drag drop object
32000          * @param {event} e The raw browser event
32001          */
32002         "enddrag" : true,
32003         /**
32004          * @event dragdrop
32005          * Fires when dragged row(s) are dropped on a valid DD target
32006          * @param {Grid} this
32007          * @param {Roo.GridDD} dd The drag drop object
32008          * @param {String} targetId The target drag drop object
32009          * @param {event} e The raw browser event
32010          */
32011         "dragdrop" : true,
32012         /**
32013          * @event dragover
32014          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
32015          * @param {Grid} this
32016          * @param {Roo.GridDD} dd The drag drop object
32017          * @param {String} targetId The target drag drop object
32018          * @param {event} e The raw browser event
32019          */
32020         "dragover" : true,
32021         /**
32022          * @event dragenter
32023          *  Fires when the dragged row(s) first cross another DD target while being dragged
32024          * @param {Grid} this
32025          * @param {Roo.GridDD} dd The drag drop object
32026          * @param {String} targetId The target drag drop object
32027          * @param {event} e The raw browser event
32028          */
32029         "dragenter" : true,
32030         /**
32031          * @event dragout
32032          * Fires when the dragged row(s) leave another DD target while being dragged
32033          * @param {Grid} this
32034          * @param {Roo.GridDD} dd The drag drop object
32035          * @param {String} targetId The target drag drop object
32036          * @param {event} e The raw browser event
32037          */
32038         "dragout" : true,
32039         /**
32040          * @event rowclass
32041          * Fires when a row is rendered, so you can change add a style to it.
32042          * @param {GridView} gridview   The grid view
32043          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
32044          */
32045         'rowclass' : true,
32046
32047         /**
32048          * @event render
32049          * Fires when the grid is rendered
32050          * @param {Grid} grid
32051          */
32052         'render' : true
32053     });
32054
32055     Roo.grid.Grid.superclass.constructor.call(this);
32056 };
32057 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
32058     
32059     /**
32060      * @cfg {String} ddGroup - drag drop group.
32061      */
32062
32063     /**
32064      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
32065      */
32066     minColumnWidth : 25,
32067
32068     /**
32069      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
32070      * <b>on initial render.</b> It is more efficient to explicitly size the columns
32071      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
32072      */
32073     autoSizeColumns : false,
32074
32075     /**
32076      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
32077      */
32078     autoSizeHeaders : true,
32079
32080     /**
32081      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
32082      */
32083     monitorWindowResize : true,
32084
32085     /**
32086      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
32087      * rows measured to get a columns size. Default is 0 (all rows).
32088      */
32089     maxRowsToMeasure : 0,
32090
32091     /**
32092      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
32093      */
32094     trackMouseOver : true,
32095
32096     /**
32097     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
32098     */
32099     
32100     /**
32101     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
32102     */
32103     enableDragDrop : false,
32104     
32105     /**
32106     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
32107     */
32108     enableColumnMove : true,
32109     
32110     /**
32111     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
32112     */
32113     enableColumnHide : true,
32114     
32115     /**
32116     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
32117     */
32118     enableRowHeightSync : false,
32119     
32120     /**
32121     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
32122     */
32123     stripeRows : true,
32124     
32125     /**
32126     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
32127     */
32128     autoHeight : false,
32129
32130     /**
32131      * @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.
32132      */
32133     autoExpandColumn : false,
32134
32135     /**
32136     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
32137     * Default is 50.
32138     */
32139     autoExpandMin : 50,
32140
32141     /**
32142     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
32143     */
32144     autoExpandMax : 1000,
32145
32146     /**
32147     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
32148     */
32149     view : null,
32150
32151     /**
32152     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
32153     */
32154     loadMask : false,
32155     /**
32156     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
32157     */
32158     dropTarget: false,
32159     
32160    
32161     
32162     // private
32163     rendered : false,
32164
32165     /**
32166     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
32167     * of a fixed width. Default is false.
32168     */
32169     /**
32170     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
32171     */
32172     /**
32173      * Called once after all setup has been completed and the grid is ready to be rendered.
32174      * @return {Roo.grid.Grid} this
32175      */
32176     render : function()
32177     {
32178         var c = this.container;
32179         // try to detect autoHeight/width mode
32180         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
32181             this.autoHeight = true;
32182         }
32183         var view = this.getView();
32184         view.init(this);
32185
32186         c.on("click", this.onClick, this);
32187         c.on("dblclick", this.onDblClick, this);
32188         c.on("contextmenu", this.onContextMenu, this);
32189         c.on("keydown", this.onKeyDown, this);
32190         if (Roo.isTouch) {
32191             c.on("touchstart", this.onTouchStart, this);
32192         }
32193
32194         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
32195
32196         this.getSelectionModel().init(this);
32197
32198         view.render();
32199
32200         if(this.loadMask){
32201             this.loadMask = new Roo.LoadMask(this.container,
32202                     Roo.apply({store:this.dataSource}, this.loadMask));
32203         }
32204         
32205         
32206         if (this.toolbar && this.toolbar.xtype) {
32207             this.toolbar.container = this.getView().getHeaderPanel(true);
32208             this.toolbar = new Roo.Toolbar(this.toolbar);
32209         }
32210         if (this.footer && this.footer.xtype) {
32211             this.footer.dataSource = this.getDataSource();
32212             this.footer.container = this.getView().getFooterPanel(true);
32213             this.footer = Roo.factory(this.footer, Roo);
32214         }
32215         if (this.dropTarget && this.dropTarget.xtype) {
32216             delete this.dropTarget.xtype;
32217             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
32218         }
32219         
32220         
32221         this.rendered = true;
32222         this.fireEvent('render', this);
32223         return this;
32224     },
32225
32226         /**
32227          * Reconfigures the grid to use a different Store and Column Model.
32228          * The View will be bound to the new objects and refreshed.
32229          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
32230          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
32231          */
32232     reconfigure : function(dataSource, colModel){
32233         if(this.loadMask){
32234             this.loadMask.destroy();
32235             this.loadMask = new Roo.LoadMask(this.container,
32236                     Roo.apply({store:dataSource}, this.loadMask));
32237         }
32238         this.view.bind(dataSource, colModel);
32239         this.dataSource = dataSource;
32240         this.colModel = colModel;
32241         this.view.refresh(true);
32242     },
32243
32244     // private
32245     onKeyDown : function(e){
32246         this.fireEvent("keydown", e);
32247     },
32248
32249     /**
32250      * Destroy this grid.
32251      * @param {Boolean} removeEl True to remove the element
32252      */
32253     destroy : function(removeEl, keepListeners){
32254         if(this.loadMask){
32255             this.loadMask.destroy();
32256         }
32257         var c = this.container;
32258         c.removeAllListeners();
32259         this.view.destroy();
32260         this.colModel.purgeListeners();
32261         if(!keepListeners){
32262             this.purgeListeners();
32263         }
32264         c.update("");
32265         if(removeEl === true){
32266             c.remove();
32267         }
32268     },
32269
32270     // private
32271     processEvent : function(name, e){
32272         // does this fire select???
32273         //Roo.log('grid:processEvent '  + name);
32274         
32275         if (name != 'touchstart' ) {
32276             this.fireEvent(name, e);    
32277         }
32278         
32279         var t = e.getTarget();
32280         var v = this.view;
32281         var header = v.findHeaderIndex(t);
32282         if(header !== false){
32283             var ename = name == 'touchstart' ? 'click' : name;
32284              
32285             this.fireEvent("header" + ename, this, header, e);
32286         }else{
32287             var row = v.findRowIndex(t);
32288             var cell = v.findCellIndex(t);
32289             if (name == 'touchstart') {
32290                 // first touch is always a click.
32291                 // hopefull this happens after selection is updated.?
32292                 name = false;
32293                 
32294                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
32295                     var cs = this.selModel.getSelectedCell();
32296                     if (row == cs[0] && cell == cs[1]){
32297                         name = 'dblclick';
32298                     }
32299                 }
32300                 if (typeof(this.selModel.getSelections) != 'undefined') {
32301                     var cs = this.selModel.getSelections();
32302                     var ds = this.dataSource;
32303                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
32304                         name = 'dblclick';
32305                     }
32306                 }
32307                 if (!name) {
32308                     return;
32309                 }
32310             }
32311             
32312             
32313             if(row !== false){
32314                 this.fireEvent("row" + name, this, row, e);
32315                 if(cell !== false){
32316                     this.fireEvent("cell" + name, this, row, cell, e);
32317                 }
32318             }
32319         }
32320     },
32321
32322     // private
32323     onClick : function(e){
32324         this.processEvent("click", e);
32325     },
32326    // private
32327     onTouchStart : function(e){
32328         this.processEvent("touchstart", e);
32329     },
32330
32331     // private
32332     onContextMenu : function(e, t){
32333         this.processEvent("contextmenu", e);
32334     },
32335
32336     // private
32337     onDblClick : function(e){
32338         this.processEvent("dblclick", e);
32339     },
32340
32341     // private
32342     walkCells : function(row, col, step, fn, scope){
32343         var cm = this.colModel, clen = cm.getColumnCount();
32344         var ds = this.dataSource, rlen = ds.getCount(), first = true;
32345         if(step < 0){
32346             if(col < 0){
32347                 row--;
32348                 first = false;
32349             }
32350             while(row >= 0){
32351                 if(!first){
32352                     col = clen-1;
32353                 }
32354                 first = false;
32355                 while(col >= 0){
32356                     if(fn.call(scope || this, row, col, cm) === true){
32357                         return [row, col];
32358                     }
32359                     col--;
32360                 }
32361                 row--;
32362             }
32363         } else {
32364             if(col >= clen){
32365                 row++;
32366                 first = false;
32367             }
32368             while(row < rlen){
32369                 if(!first){
32370                     col = 0;
32371                 }
32372                 first = false;
32373                 while(col < clen){
32374                     if(fn.call(scope || this, row, col, cm) === true){
32375                         return [row, col];
32376                     }
32377                     col++;
32378                 }
32379                 row++;
32380             }
32381         }
32382         return null;
32383     },
32384
32385     // private
32386     getSelections : function(){
32387         return this.selModel.getSelections();
32388     },
32389
32390     /**
32391      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
32392      * but if manual update is required this method will initiate it.
32393      */
32394     autoSize : function(){
32395         if(this.rendered){
32396             this.view.layout();
32397             if(this.view.adjustForScroll){
32398                 this.view.adjustForScroll();
32399             }
32400         }
32401     },
32402
32403     /**
32404      * Returns the grid's underlying element.
32405      * @return {Element} The element
32406      */
32407     getGridEl : function(){
32408         return this.container;
32409     },
32410
32411     // private for compatibility, overridden by editor grid
32412     stopEditing : function(){},
32413
32414     /**
32415      * Returns the grid's SelectionModel.
32416      * @return {SelectionModel}
32417      */
32418     getSelectionModel : function(){
32419         if(!this.selModel){
32420             this.selModel = new Roo.grid.RowSelectionModel();
32421         }
32422         return this.selModel;
32423     },
32424
32425     /**
32426      * Returns the grid's DataSource.
32427      * @return {DataSource}
32428      */
32429     getDataSource : function(){
32430         return this.dataSource;
32431     },
32432
32433     /**
32434      * Returns the grid's ColumnModel.
32435      * @return {ColumnModel}
32436      */
32437     getColumnModel : function(){
32438         return this.colModel;
32439     },
32440
32441     /**
32442      * Returns the grid's GridView object.
32443      * @return {GridView}
32444      */
32445     getView : function(){
32446         if(!this.view){
32447             this.view = new Roo.grid.GridView(this.viewConfig);
32448         }
32449         return this.view;
32450     },
32451     /**
32452      * Called to get grid's drag proxy text, by default returns this.ddText.
32453      * @return {String}
32454      */
32455     getDragDropText : function(){
32456         var count = this.selModel.getCount();
32457         return String.format(this.ddText, count, count == 1 ? '' : 's');
32458     }
32459 });
32460 /**
32461  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
32462  * %0 is replaced with the number of selected rows.
32463  * @type String
32464  */
32465 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
32466  * Based on:
32467  * Ext JS Library 1.1.1
32468  * Copyright(c) 2006-2007, Ext JS, LLC.
32469  *
32470  * Originally Released Under LGPL - original licence link has changed is not relivant.
32471  *
32472  * Fork - LGPL
32473  * <script type="text/javascript">
32474  */
32475  
32476 Roo.grid.AbstractGridView = function(){
32477         this.grid = null;
32478         
32479         this.events = {
32480             "beforerowremoved" : true,
32481             "beforerowsinserted" : true,
32482             "beforerefresh" : true,
32483             "rowremoved" : true,
32484             "rowsinserted" : true,
32485             "rowupdated" : true,
32486             "refresh" : true
32487         };
32488     Roo.grid.AbstractGridView.superclass.constructor.call(this);
32489 };
32490
32491 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
32492     rowClass : "x-grid-row",
32493     cellClass : "x-grid-cell",
32494     tdClass : "x-grid-td",
32495     hdClass : "x-grid-hd",
32496     splitClass : "x-grid-hd-split",
32497     
32498     init: function(grid){
32499         this.grid = grid;
32500                 var cid = this.grid.getGridEl().id;
32501         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
32502         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
32503         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
32504         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
32505         },
32506         
32507     getColumnRenderers : function(){
32508         var renderers = [];
32509         var cm = this.grid.colModel;
32510         var colCount = cm.getColumnCount();
32511         for(var i = 0; i < colCount; i++){
32512             renderers[i] = cm.getRenderer(i);
32513         }
32514         return renderers;
32515     },
32516     
32517     getColumnIds : function(){
32518         var ids = [];
32519         var cm = this.grid.colModel;
32520         var colCount = cm.getColumnCount();
32521         for(var i = 0; i < colCount; i++){
32522             ids[i] = cm.getColumnId(i);
32523         }
32524         return ids;
32525     },
32526     
32527     getDataIndexes : function(){
32528         if(!this.indexMap){
32529             this.indexMap = this.buildIndexMap();
32530         }
32531         return this.indexMap.colToData;
32532     },
32533     
32534     getColumnIndexByDataIndex : function(dataIndex){
32535         if(!this.indexMap){
32536             this.indexMap = this.buildIndexMap();
32537         }
32538         return this.indexMap.dataToCol[dataIndex];
32539     },
32540     
32541     /**
32542      * Set a css style for a column dynamically. 
32543      * @param {Number} colIndex The index of the column
32544      * @param {String} name The css property name
32545      * @param {String} value The css value
32546      */
32547     setCSSStyle : function(colIndex, name, value){
32548         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
32549         Roo.util.CSS.updateRule(selector, name, value);
32550     },
32551     
32552     generateRules : function(cm){
32553         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
32554         Roo.util.CSS.removeStyleSheet(rulesId);
32555         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
32556             var cid = cm.getColumnId(i);
32557             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
32558                          this.tdSelector, cid, " {\n}\n",
32559                          this.hdSelector, cid, " {\n}\n",
32560                          this.splitSelector, cid, " {\n}\n");
32561         }
32562         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
32563     }
32564 });/*
32565  * Based on:
32566  * Ext JS Library 1.1.1
32567  * Copyright(c) 2006-2007, Ext JS, LLC.
32568  *
32569  * Originally Released Under LGPL - original licence link has changed is not relivant.
32570  *
32571  * Fork - LGPL
32572  * <script type="text/javascript">
32573  */
32574
32575 // private
32576 // This is a support class used internally by the Grid components
32577 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
32578     this.grid = grid;
32579     this.view = grid.getView();
32580     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
32581     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
32582     if(hd2){
32583         this.setHandleElId(Roo.id(hd));
32584         this.setOuterHandleElId(Roo.id(hd2));
32585     }
32586     this.scroll = false;
32587 };
32588 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
32589     maxDragWidth: 120,
32590     getDragData : function(e){
32591         var t = Roo.lib.Event.getTarget(e);
32592         var h = this.view.findHeaderCell(t);
32593         if(h){
32594             return {ddel: h.firstChild, header:h};
32595         }
32596         return false;
32597     },
32598
32599     onInitDrag : function(e){
32600         this.view.headersDisabled = true;
32601         var clone = this.dragData.ddel.cloneNode(true);
32602         clone.id = Roo.id();
32603         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
32604         this.proxy.update(clone);
32605         return true;
32606     },
32607
32608     afterValidDrop : function(){
32609         var v = this.view;
32610         setTimeout(function(){
32611             v.headersDisabled = false;
32612         }, 50);
32613     },
32614
32615     afterInvalidDrop : function(){
32616         var v = this.view;
32617         setTimeout(function(){
32618             v.headersDisabled = false;
32619         }, 50);
32620     }
32621 });
32622 /*
32623  * Based on:
32624  * Ext JS Library 1.1.1
32625  * Copyright(c) 2006-2007, Ext JS, LLC.
32626  *
32627  * Originally Released Under LGPL - original licence link has changed is not relivant.
32628  *
32629  * Fork - LGPL
32630  * <script type="text/javascript">
32631  */
32632 // private
32633 // This is a support class used internally by the Grid components
32634 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
32635     this.grid = grid;
32636     this.view = grid.getView();
32637     // split the proxies so they don't interfere with mouse events
32638     this.proxyTop = Roo.DomHelper.append(document.body, {
32639         cls:"col-move-top", html:"&#160;"
32640     }, true);
32641     this.proxyBottom = Roo.DomHelper.append(document.body, {
32642         cls:"col-move-bottom", html:"&#160;"
32643     }, true);
32644     this.proxyTop.hide = this.proxyBottom.hide = function(){
32645         this.setLeftTop(-100,-100);
32646         this.setStyle("visibility", "hidden");
32647     };
32648     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
32649     // temporarily disabled
32650     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
32651     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
32652 };
32653 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
32654     proxyOffsets : [-4, -9],
32655     fly: Roo.Element.fly,
32656
32657     getTargetFromEvent : function(e){
32658         var t = Roo.lib.Event.getTarget(e);
32659         var cindex = this.view.findCellIndex(t);
32660         if(cindex !== false){
32661             return this.view.getHeaderCell(cindex);
32662         }
32663         return null;
32664     },
32665
32666     nextVisible : function(h){
32667         var v = this.view, cm = this.grid.colModel;
32668         h = h.nextSibling;
32669         while(h){
32670             if(!cm.isHidden(v.getCellIndex(h))){
32671                 return h;
32672             }
32673             h = h.nextSibling;
32674         }
32675         return null;
32676     },
32677
32678     prevVisible : function(h){
32679         var v = this.view, cm = this.grid.colModel;
32680         h = h.prevSibling;
32681         while(h){
32682             if(!cm.isHidden(v.getCellIndex(h))){
32683                 return h;
32684             }
32685             h = h.prevSibling;
32686         }
32687         return null;
32688     },
32689
32690     positionIndicator : function(h, n, e){
32691         var x = Roo.lib.Event.getPageX(e);
32692         var r = Roo.lib.Dom.getRegion(n.firstChild);
32693         var px, pt, py = r.top + this.proxyOffsets[1];
32694         if((r.right - x) <= (r.right-r.left)/2){
32695             px = r.right+this.view.borderWidth;
32696             pt = "after";
32697         }else{
32698             px = r.left;
32699             pt = "before";
32700         }
32701         var oldIndex = this.view.getCellIndex(h);
32702         var newIndex = this.view.getCellIndex(n);
32703
32704         if(this.grid.colModel.isFixed(newIndex)){
32705             return false;
32706         }
32707
32708         var locked = this.grid.colModel.isLocked(newIndex);
32709
32710         if(pt == "after"){
32711             newIndex++;
32712         }
32713         if(oldIndex < newIndex){
32714             newIndex--;
32715         }
32716         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
32717             return false;
32718         }
32719         px +=  this.proxyOffsets[0];
32720         this.proxyTop.setLeftTop(px, py);
32721         this.proxyTop.show();
32722         if(!this.bottomOffset){
32723             this.bottomOffset = this.view.mainHd.getHeight();
32724         }
32725         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
32726         this.proxyBottom.show();
32727         return pt;
32728     },
32729
32730     onNodeEnter : function(n, dd, e, data){
32731         if(data.header != n){
32732             this.positionIndicator(data.header, n, e);
32733         }
32734     },
32735
32736     onNodeOver : function(n, dd, e, data){
32737         var result = false;
32738         if(data.header != n){
32739             result = this.positionIndicator(data.header, n, e);
32740         }
32741         if(!result){
32742             this.proxyTop.hide();
32743             this.proxyBottom.hide();
32744         }
32745         return result ? this.dropAllowed : this.dropNotAllowed;
32746     },
32747
32748     onNodeOut : function(n, dd, e, data){
32749         this.proxyTop.hide();
32750         this.proxyBottom.hide();
32751     },
32752
32753     onNodeDrop : function(n, dd, e, data){
32754         var h = data.header;
32755         if(h != n){
32756             var cm = this.grid.colModel;
32757             var x = Roo.lib.Event.getPageX(e);
32758             var r = Roo.lib.Dom.getRegion(n.firstChild);
32759             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
32760             var oldIndex = this.view.getCellIndex(h);
32761             var newIndex = this.view.getCellIndex(n);
32762             var locked = cm.isLocked(newIndex);
32763             if(pt == "after"){
32764                 newIndex++;
32765             }
32766             if(oldIndex < newIndex){
32767                 newIndex--;
32768             }
32769             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
32770                 return false;
32771             }
32772             cm.setLocked(oldIndex, locked, true);
32773             cm.moveColumn(oldIndex, newIndex);
32774             this.grid.fireEvent("columnmove", oldIndex, newIndex);
32775             return true;
32776         }
32777         return false;
32778     }
32779 });
32780 /*
32781  * Based on:
32782  * Ext JS Library 1.1.1
32783  * Copyright(c) 2006-2007, Ext JS, LLC.
32784  *
32785  * Originally Released Under LGPL - original licence link has changed is not relivant.
32786  *
32787  * Fork - LGPL
32788  * <script type="text/javascript">
32789  */
32790   
32791 /**
32792  * @class Roo.grid.GridView
32793  * @extends Roo.util.Observable
32794  *
32795  * @constructor
32796  * @param {Object} config
32797  */
32798 Roo.grid.GridView = function(config){
32799     Roo.grid.GridView.superclass.constructor.call(this);
32800     this.el = null;
32801
32802     Roo.apply(this, config);
32803 };
32804
32805 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
32806
32807     unselectable :  'unselectable="on"',
32808     unselectableCls :  'x-unselectable',
32809     
32810     
32811     rowClass : "x-grid-row",
32812
32813     cellClass : "x-grid-col",
32814
32815     tdClass : "x-grid-td",
32816
32817     hdClass : "x-grid-hd",
32818
32819     splitClass : "x-grid-split",
32820
32821     sortClasses : ["sort-asc", "sort-desc"],
32822
32823     enableMoveAnim : false,
32824
32825     hlColor: "C3DAF9",
32826
32827     dh : Roo.DomHelper,
32828
32829     fly : Roo.Element.fly,
32830
32831     css : Roo.util.CSS,
32832
32833     borderWidth: 1,
32834
32835     splitOffset: 3,
32836
32837     scrollIncrement : 22,
32838
32839     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
32840
32841     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
32842
32843     bind : function(ds, cm){
32844         if(this.ds){
32845             this.ds.un("load", this.onLoad, this);
32846             this.ds.un("datachanged", this.onDataChange, this);
32847             this.ds.un("add", this.onAdd, this);
32848             this.ds.un("remove", this.onRemove, this);
32849             this.ds.un("update", this.onUpdate, this);
32850             this.ds.un("clear", this.onClear, this);
32851         }
32852         if(ds){
32853             ds.on("load", this.onLoad, this);
32854             ds.on("datachanged", this.onDataChange, this);
32855             ds.on("add", this.onAdd, this);
32856             ds.on("remove", this.onRemove, this);
32857             ds.on("update", this.onUpdate, this);
32858             ds.on("clear", this.onClear, this);
32859         }
32860         this.ds = ds;
32861
32862         if(this.cm){
32863             this.cm.un("widthchange", this.onColWidthChange, this);
32864             this.cm.un("headerchange", this.onHeaderChange, this);
32865             this.cm.un("hiddenchange", this.onHiddenChange, this);
32866             this.cm.un("columnmoved", this.onColumnMove, this);
32867             this.cm.un("columnlockchange", this.onColumnLock, this);
32868         }
32869         if(cm){
32870             this.generateRules(cm);
32871             cm.on("widthchange", this.onColWidthChange, this);
32872             cm.on("headerchange", this.onHeaderChange, this);
32873             cm.on("hiddenchange", this.onHiddenChange, this);
32874             cm.on("columnmoved", this.onColumnMove, this);
32875             cm.on("columnlockchange", this.onColumnLock, this);
32876         }
32877         this.cm = cm;
32878     },
32879
32880     init: function(grid){
32881         Roo.grid.GridView.superclass.init.call(this, grid);
32882
32883         this.bind(grid.dataSource, grid.colModel);
32884
32885         grid.on("headerclick", this.handleHeaderClick, this);
32886
32887         if(grid.trackMouseOver){
32888             grid.on("mouseover", this.onRowOver, this);
32889             grid.on("mouseout", this.onRowOut, this);
32890         }
32891         grid.cancelTextSelection = function(){};
32892         this.gridId = grid.id;
32893
32894         var tpls = this.templates || {};
32895
32896         if(!tpls.master){
32897             tpls.master = new Roo.Template(
32898                '<div class="x-grid" hidefocus="true">',
32899                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
32900                   '<div class="x-grid-topbar"></div>',
32901                   '<div class="x-grid-scroller"><div></div></div>',
32902                   '<div class="x-grid-locked">',
32903                       '<div class="x-grid-header">{lockedHeader}</div>',
32904                       '<div class="x-grid-body">{lockedBody}</div>',
32905                   "</div>",
32906                   '<div class="x-grid-viewport">',
32907                       '<div class="x-grid-header">{header}</div>',
32908                       '<div class="x-grid-body">{body}</div>',
32909                   "</div>",
32910                   '<div class="x-grid-bottombar"></div>',
32911                  
32912                   '<div class="x-grid-resize-proxy">&#160;</div>',
32913                "</div>"
32914             );
32915             tpls.master.disableformats = true;
32916         }
32917
32918         if(!tpls.header){
32919             tpls.header = new Roo.Template(
32920                '<table border="0" cellspacing="0" cellpadding="0">',
32921                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
32922                "</table>{splits}"
32923             );
32924             tpls.header.disableformats = true;
32925         }
32926         tpls.header.compile();
32927
32928         if(!tpls.hcell){
32929             tpls.hcell = new Roo.Template(
32930                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
32931                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
32932                 "</div></td>"
32933              );
32934              tpls.hcell.disableFormats = true;
32935         }
32936         tpls.hcell.compile();
32937
32938         if(!tpls.hsplit){
32939             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
32940                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
32941             tpls.hsplit.disableFormats = true;
32942         }
32943         tpls.hsplit.compile();
32944
32945         if(!tpls.body){
32946             tpls.body = new Roo.Template(
32947                '<table border="0" cellspacing="0" cellpadding="0">',
32948                "<tbody>{rows}</tbody>",
32949                "</table>"
32950             );
32951             tpls.body.disableFormats = true;
32952         }
32953         tpls.body.compile();
32954
32955         if(!tpls.row){
32956             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
32957             tpls.row.disableFormats = true;
32958         }
32959         tpls.row.compile();
32960
32961         if(!tpls.cell){
32962             tpls.cell = new Roo.Template(
32963                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
32964                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
32965                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
32966                 "</td>"
32967             );
32968             tpls.cell.disableFormats = true;
32969         }
32970         tpls.cell.compile();
32971
32972         this.templates = tpls;
32973     },
32974
32975     // remap these for backwards compat
32976     onColWidthChange : function(){
32977         this.updateColumns.apply(this, arguments);
32978     },
32979     onHeaderChange : function(){
32980         this.updateHeaders.apply(this, arguments);
32981     }, 
32982     onHiddenChange : function(){
32983         this.handleHiddenChange.apply(this, arguments);
32984     },
32985     onColumnMove : function(){
32986         this.handleColumnMove.apply(this, arguments);
32987     },
32988     onColumnLock : function(){
32989         this.handleLockChange.apply(this, arguments);
32990     },
32991
32992     onDataChange : function(){
32993         this.refresh();
32994         this.updateHeaderSortState();
32995     },
32996
32997     onClear : function(){
32998         this.refresh();
32999     },
33000
33001     onUpdate : function(ds, record){
33002         this.refreshRow(record);
33003     },
33004
33005     refreshRow : function(record){
33006         var ds = this.ds, index;
33007         if(typeof record == 'number'){
33008             index = record;
33009             record = ds.getAt(index);
33010         }else{
33011             index = ds.indexOf(record);
33012         }
33013         this.insertRows(ds, index, index, true);
33014         this.onRemove(ds, record, index+1, true);
33015         this.syncRowHeights(index, index);
33016         this.layout();
33017         this.fireEvent("rowupdated", this, index, record);
33018     },
33019
33020     onAdd : function(ds, records, index){
33021         this.insertRows(ds, index, index + (records.length-1));
33022     },
33023
33024     onRemove : function(ds, record, index, isUpdate){
33025         if(isUpdate !== true){
33026             this.fireEvent("beforerowremoved", this, index, record);
33027         }
33028         var bt = this.getBodyTable(), lt = this.getLockedTable();
33029         if(bt.rows[index]){
33030             bt.firstChild.removeChild(bt.rows[index]);
33031         }
33032         if(lt.rows[index]){
33033             lt.firstChild.removeChild(lt.rows[index]);
33034         }
33035         if(isUpdate !== true){
33036             this.stripeRows(index);
33037             this.syncRowHeights(index, index);
33038             this.layout();
33039             this.fireEvent("rowremoved", this, index, record);
33040         }
33041     },
33042
33043     onLoad : function(){
33044         this.scrollToTop();
33045     },
33046
33047     /**
33048      * Scrolls the grid to the top
33049      */
33050     scrollToTop : function(){
33051         if(this.scroller){
33052             this.scroller.dom.scrollTop = 0;
33053             this.syncScroll();
33054         }
33055     },
33056
33057     /**
33058      * Gets a panel in the header of the grid that can be used for toolbars etc.
33059      * After modifying the contents of this panel a call to grid.autoSize() may be
33060      * required to register any changes in size.
33061      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
33062      * @return Roo.Element
33063      */
33064     getHeaderPanel : function(doShow){
33065         if(doShow){
33066             this.headerPanel.show();
33067         }
33068         return this.headerPanel;
33069     },
33070
33071     /**
33072      * Gets a panel in the footer of the grid that can be used for toolbars etc.
33073      * After modifying the contents of this panel a call to grid.autoSize() may be
33074      * required to register any changes in size.
33075      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
33076      * @return Roo.Element
33077      */
33078     getFooterPanel : function(doShow){
33079         if(doShow){
33080             this.footerPanel.show();
33081         }
33082         return this.footerPanel;
33083     },
33084
33085     initElements : function(){
33086         var E = Roo.Element;
33087         var el = this.grid.getGridEl().dom.firstChild;
33088         var cs = el.childNodes;
33089
33090         this.el = new E(el);
33091         
33092          this.focusEl = new E(el.firstChild);
33093         this.focusEl.swallowEvent("click", true);
33094         
33095         this.headerPanel = new E(cs[1]);
33096         this.headerPanel.enableDisplayMode("block");
33097
33098         this.scroller = new E(cs[2]);
33099         this.scrollSizer = new E(this.scroller.dom.firstChild);
33100
33101         this.lockedWrap = new E(cs[3]);
33102         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
33103         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
33104
33105         this.mainWrap = new E(cs[4]);
33106         this.mainHd = new E(this.mainWrap.dom.firstChild);
33107         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
33108
33109         this.footerPanel = new E(cs[5]);
33110         this.footerPanel.enableDisplayMode("block");
33111
33112         this.resizeProxy = new E(cs[6]);
33113
33114         this.headerSelector = String.format(
33115            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
33116            this.lockedHd.id, this.mainHd.id
33117         );
33118
33119         this.splitterSelector = String.format(
33120            '#{0} div.x-grid-split, #{1} div.x-grid-split',
33121            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
33122         );
33123     },
33124     idToCssName : function(s)
33125     {
33126         return s.replace(/[^a-z0-9]+/ig, '-');
33127     },
33128
33129     getHeaderCell : function(index){
33130         return Roo.DomQuery.select(this.headerSelector)[index];
33131     },
33132
33133     getHeaderCellMeasure : function(index){
33134         return this.getHeaderCell(index).firstChild;
33135     },
33136
33137     getHeaderCellText : function(index){
33138         return this.getHeaderCell(index).firstChild.firstChild;
33139     },
33140
33141     getLockedTable : function(){
33142         return this.lockedBody.dom.firstChild;
33143     },
33144
33145     getBodyTable : function(){
33146         return this.mainBody.dom.firstChild;
33147     },
33148
33149     getLockedRow : function(index){
33150         return this.getLockedTable().rows[index];
33151     },
33152
33153     getRow : function(index){
33154         return this.getBodyTable().rows[index];
33155     },
33156
33157     getRowComposite : function(index){
33158         if(!this.rowEl){
33159             this.rowEl = new Roo.CompositeElementLite();
33160         }
33161         var els = [], lrow, mrow;
33162         if(lrow = this.getLockedRow(index)){
33163             els.push(lrow);
33164         }
33165         if(mrow = this.getRow(index)){
33166             els.push(mrow);
33167         }
33168         this.rowEl.elements = els;
33169         return this.rowEl;
33170     },
33171     /**
33172      * Gets the 'td' of the cell
33173      * 
33174      * @param {Integer} rowIndex row to select
33175      * @param {Integer} colIndex column to select
33176      * 
33177      * @return {Object} 
33178      */
33179     getCell : function(rowIndex, colIndex){
33180         var locked = this.cm.getLockedCount();
33181         var source;
33182         if(colIndex < locked){
33183             source = this.lockedBody.dom.firstChild;
33184         }else{
33185             source = this.mainBody.dom.firstChild;
33186             colIndex -= locked;
33187         }
33188         return source.rows[rowIndex].childNodes[colIndex];
33189     },
33190
33191     getCellText : function(rowIndex, colIndex){
33192         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
33193     },
33194
33195     getCellBox : function(cell){
33196         var b = this.fly(cell).getBox();
33197         if(Roo.isOpera){ // opera fails to report the Y
33198             b.y = cell.offsetTop + this.mainBody.getY();
33199         }
33200         return b;
33201     },
33202
33203     getCellIndex : function(cell){
33204         var id = String(cell.className).match(this.cellRE);
33205         if(id){
33206             return parseInt(id[1], 10);
33207         }
33208         return 0;
33209     },
33210
33211     findHeaderIndex : function(n){
33212         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33213         return r ? this.getCellIndex(r) : false;
33214     },
33215
33216     findHeaderCell : function(n){
33217         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33218         return r ? r : false;
33219     },
33220
33221     findRowIndex : function(n){
33222         if(!n){
33223             return false;
33224         }
33225         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
33226         return r ? r.rowIndex : false;
33227     },
33228
33229     findCellIndex : function(node){
33230         var stop = this.el.dom;
33231         while(node && node != stop){
33232             if(this.findRE.test(node.className)){
33233                 return this.getCellIndex(node);
33234             }
33235             node = node.parentNode;
33236         }
33237         return false;
33238     },
33239
33240     getColumnId : function(index){
33241         return this.cm.getColumnId(index);
33242     },
33243
33244     getSplitters : function()
33245     {
33246         if(this.splitterSelector){
33247            return Roo.DomQuery.select(this.splitterSelector);
33248         }else{
33249             return null;
33250       }
33251     },
33252
33253     getSplitter : function(index){
33254         return this.getSplitters()[index];
33255     },
33256
33257     onRowOver : function(e, t){
33258         var row;
33259         if((row = this.findRowIndex(t)) !== false){
33260             this.getRowComposite(row).addClass("x-grid-row-over");
33261         }
33262     },
33263
33264     onRowOut : function(e, t){
33265         var row;
33266         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
33267             this.getRowComposite(row).removeClass("x-grid-row-over");
33268         }
33269     },
33270
33271     renderHeaders : function(){
33272         var cm = this.cm;
33273         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
33274         var cb = [], lb = [], sb = [], lsb = [], p = {};
33275         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33276             p.cellId = "x-grid-hd-0-" + i;
33277             p.splitId = "x-grid-csplit-0-" + i;
33278             p.id = cm.getColumnId(i);
33279             p.value = cm.getColumnHeader(i) || "";
33280             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
33281             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
33282             if(!cm.isLocked(i)){
33283                 cb[cb.length] = ct.apply(p);
33284                 sb[sb.length] = st.apply(p);
33285             }else{
33286                 lb[lb.length] = ct.apply(p);
33287                 lsb[lsb.length] = st.apply(p);
33288             }
33289         }
33290         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
33291                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
33292     },
33293
33294     updateHeaders : function(){
33295         var html = this.renderHeaders();
33296         this.lockedHd.update(html[0]);
33297         this.mainHd.update(html[1]);
33298     },
33299
33300     /**
33301      * Focuses the specified row.
33302      * @param {Number} row The row index
33303      */
33304     focusRow : function(row)
33305     {
33306         //Roo.log('GridView.focusRow');
33307         var x = this.scroller.dom.scrollLeft;
33308         this.focusCell(row, 0, false);
33309         this.scroller.dom.scrollLeft = x;
33310     },
33311
33312     /**
33313      * Focuses the specified cell.
33314      * @param {Number} row The row index
33315      * @param {Number} col The column index
33316      * @param {Boolean} hscroll false to disable horizontal scrolling
33317      */
33318     focusCell : function(row, col, hscroll)
33319     {
33320         //Roo.log('GridView.focusCell');
33321         var el = this.ensureVisible(row, col, hscroll);
33322         this.focusEl.alignTo(el, "tl-tl");
33323         if(Roo.isGecko){
33324             this.focusEl.focus();
33325         }else{
33326             this.focusEl.focus.defer(1, this.focusEl);
33327         }
33328     },
33329
33330     /**
33331      * Scrolls the specified cell into view
33332      * @param {Number} row The row index
33333      * @param {Number} col The column index
33334      * @param {Boolean} hscroll false to disable horizontal scrolling
33335      */
33336     ensureVisible : function(row, col, hscroll)
33337     {
33338         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
33339         //return null; //disable for testing.
33340         if(typeof row != "number"){
33341             row = row.rowIndex;
33342         }
33343         if(row < 0 && row >= this.ds.getCount()){
33344             return  null;
33345         }
33346         col = (col !== undefined ? col : 0);
33347         var cm = this.grid.colModel;
33348         while(cm.isHidden(col)){
33349             col++;
33350         }
33351
33352         var el = this.getCell(row, col);
33353         if(!el){
33354             return null;
33355         }
33356         var c = this.scroller.dom;
33357
33358         var ctop = parseInt(el.offsetTop, 10);
33359         var cleft = parseInt(el.offsetLeft, 10);
33360         var cbot = ctop + el.offsetHeight;
33361         var cright = cleft + el.offsetWidth;
33362         
33363         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
33364         var stop = parseInt(c.scrollTop, 10);
33365         var sleft = parseInt(c.scrollLeft, 10);
33366         var sbot = stop + ch;
33367         var sright = sleft + c.clientWidth;
33368         /*
33369         Roo.log('GridView.ensureVisible:' +
33370                 ' ctop:' + ctop +
33371                 ' c.clientHeight:' + c.clientHeight +
33372                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
33373                 ' stop:' + stop +
33374                 ' cbot:' + cbot +
33375                 ' sbot:' + sbot +
33376                 ' ch:' + ch  
33377                 );
33378         */
33379         if(ctop < stop){
33380              c.scrollTop = ctop;
33381             //Roo.log("set scrolltop to ctop DISABLE?");
33382         }else if(cbot > sbot){
33383             //Roo.log("set scrolltop to cbot-ch");
33384             c.scrollTop = cbot-ch;
33385         }
33386         
33387         if(hscroll !== false){
33388             if(cleft < sleft){
33389                 c.scrollLeft = cleft;
33390             }else if(cright > sright){
33391                 c.scrollLeft = cright-c.clientWidth;
33392             }
33393         }
33394          
33395         return el;
33396     },
33397
33398     updateColumns : function(){
33399         this.grid.stopEditing();
33400         var cm = this.grid.colModel, colIds = this.getColumnIds();
33401         //var totalWidth = cm.getTotalWidth();
33402         var pos = 0;
33403         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33404             //if(cm.isHidden(i)) continue;
33405             var w = cm.getColumnWidth(i);
33406             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33407             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33408         }
33409         this.updateSplitters();
33410     },
33411
33412     generateRules : function(cm){
33413         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
33414         Roo.util.CSS.removeStyleSheet(rulesId);
33415         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33416             var cid = cm.getColumnId(i);
33417             var align = '';
33418             if(cm.config[i].align){
33419                 align = 'text-align:'+cm.config[i].align+';';
33420             }
33421             var hidden = '';
33422             if(cm.isHidden(i)){
33423                 hidden = 'display:none;';
33424             }
33425             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
33426             ruleBuf.push(
33427                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
33428                     this.hdSelector, cid, " {\n", align, width, "}\n",
33429                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
33430                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
33431         }
33432         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33433     },
33434
33435     updateSplitters : function(){
33436         var cm = this.cm, s = this.getSplitters();
33437         if(s){ // splitters not created yet
33438             var pos = 0, locked = true;
33439             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33440                 if(cm.isHidden(i)) {
33441                     continue;
33442                 }
33443                 var w = cm.getColumnWidth(i); // make sure it's a number
33444                 if(!cm.isLocked(i) && locked){
33445                     pos = 0;
33446                     locked = false;
33447                 }
33448                 pos += w;
33449                 s[i].style.left = (pos-this.splitOffset) + "px";
33450             }
33451         }
33452     },
33453
33454     handleHiddenChange : function(colModel, colIndex, hidden){
33455         if(hidden){
33456             this.hideColumn(colIndex);
33457         }else{
33458             this.unhideColumn(colIndex);
33459         }
33460     },
33461
33462     hideColumn : function(colIndex){
33463         var cid = this.getColumnId(colIndex);
33464         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
33465         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
33466         if(Roo.isSafari){
33467             this.updateHeaders();
33468         }
33469         this.updateSplitters();
33470         this.layout();
33471     },
33472
33473     unhideColumn : function(colIndex){
33474         var cid = this.getColumnId(colIndex);
33475         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
33476         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
33477
33478         if(Roo.isSafari){
33479             this.updateHeaders();
33480         }
33481         this.updateSplitters();
33482         this.layout();
33483     },
33484
33485     insertRows : function(dm, firstRow, lastRow, isUpdate){
33486         if(firstRow == 0 && lastRow == dm.getCount()-1){
33487             this.refresh();
33488         }else{
33489             if(!isUpdate){
33490                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
33491             }
33492             var s = this.getScrollState();
33493             var markup = this.renderRows(firstRow, lastRow);
33494             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
33495             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
33496             this.restoreScroll(s);
33497             if(!isUpdate){
33498                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
33499                 this.syncRowHeights(firstRow, lastRow);
33500                 this.stripeRows(firstRow);
33501                 this.layout();
33502             }
33503         }
33504     },
33505
33506     bufferRows : function(markup, target, index){
33507         var before = null, trows = target.rows, tbody = target.tBodies[0];
33508         if(index < trows.length){
33509             before = trows[index];
33510         }
33511         var b = document.createElement("div");
33512         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
33513         var rows = b.firstChild.rows;
33514         for(var i = 0, len = rows.length; i < len; i++){
33515             if(before){
33516                 tbody.insertBefore(rows[0], before);
33517             }else{
33518                 tbody.appendChild(rows[0]);
33519             }
33520         }
33521         b.innerHTML = "";
33522         b = null;
33523     },
33524
33525     deleteRows : function(dm, firstRow, lastRow){
33526         if(dm.getRowCount()<1){
33527             this.fireEvent("beforerefresh", this);
33528             this.mainBody.update("");
33529             this.lockedBody.update("");
33530             this.fireEvent("refresh", this);
33531         }else{
33532             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
33533             var bt = this.getBodyTable();
33534             var tbody = bt.firstChild;
33535             var rows = bt.rows;
33536             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
33537                 tbody.removeChild(rows[firstRow]);
33538             }
33539             this.stripeRows(firstRow);
33540             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
33541         }
33542     },
33543
33544     updateRows : function(dataSource, firstRow, lastRow){
33545         var s = this.getScrollState();
33546         this.refresh();
33547         this.restoreScroll(s);
33548     },
33549
33550     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
33551         if(!noRefresh){
33552            this.refresh();
33553         }
33554         this.updateHeaderSortState();
33555     },
33556
33557     getScrollState : function(){
33558         
33559         var sb = this.scroller.dom;
33560         return {left: sb.scrollLeft, top: sb.scrollTop};
33561     },
33562
33563     stripeRows : function(startRow){
33564         if(!this.grid.stripeRows || this.ds.getCount() < 1){
33565             return;
33566         }
33567         startRow = startRow || 0;
33568         var rows = this.getBodyTable().rows;
33569         var lrows = this.getLockedTable().rows;
33570         var cls = ' x-grid-row-alt ';
33571         for(var i = startRow, len = rows.length; i < len; i++){
33572             var row = rows[i], lrow = lrows[i];
33573             var isAlt = ((i+1) % 2 == 0);
33574             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
33575             if(isAlt == hasAlt){
33576                 continue;
33577             }
33578             if(isAlt){
33579                 row.className += " x-grid-row-alt";
33580             }else{
33581                 row.className = row.className.replace("x-grid-row-alt", "");
33582             }
33583             if(lrow){
33584                 lrow.className = row.className;
33585             }
33586         }
33587     },
33588
33589     restoreScroll : function(state){
33590         //Roo.log('GridView.restoreScroll');
33591         var sb = this.scroller.dom;
33592         sb.scrollLeft = state.left;
33593         sb.scrollTop = state.top;
33594         this.syncScroll();
33595     },
33596
33597     syncScroll : function(){
33598         //Roo.log('GridView.syncScroll');
33599         var sb = this.scroller.dom;
33600         var sh = this.mainHd.dom;
33601         var bs = this.mainBody.dom;
33602         var lv = this.lockedBody.dom;
33603         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
33604         lv.scrollTop = bs.scrollTop = sb.scrollTop;
33605     },
33606
33607     handleScroll : function(e){
33608         this.syncScroll();
33609         var sb = this.scroller.dom;
33610         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
33611         e.stopEvent();
33612     },
33613
33614     handleWheel : function(e){
33615         var d = e.getWheelDelta();
33616         this.scroller.dom.scrollTop -= d*22;
33617         // set this here to prevent jumpy scrolling on large tables
33618         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
33619         e.stopEvent();
33620     },
33621
33622     renderRows : function(startRow, endRow){
33623         // pull in all the crap needed to render rows
33624         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
33625         var colCount = cm.getColumnCount();
33626
33627         if(ds.getCount() < 1){
33628             return ["", ""];
33629         }
33630
33631         // build a map for all the columns
33632         var cs = [];
33633         for(var i = 0; i < colCount; i++){
33634             var name = cm.getDataIndex(i);
33635             cs[i] = {
33636                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
33637                 renderer : cm.getRenderer(i),
33638                 id : cm.getColumnId(i),
33639                 locked : cm.isLocked(i),
33640                 has_editor : cm.isCellEditable(i)
33641             };
33642         }
33643
33644         startRow = startRow || 0;
33645         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
33646
33647         // records to render
33648         var rs = ds.getRange(startRow, endRow);
33649
33650         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
33651     },
33652
33653     // As much as I hate to duplicate code, this was branched because FireFox really hates
33654     // [].join("") on strings. The performance difference was substantial enough to
33655     // branch this function
33656     doRender : Roo.isGecko ?
33657             function(cs, rs, ds, startRow, colCount, stripe){
33658                 var ts = this.templates, ct = ts.cell, rt = ts.row;
33659                 // buffers
33660                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
33661                 
33662                 var hasListener = this.grid.hasListener('rowclass');
33663                 var rowcfg = {};
33664                 for(var j = 0, len = rs.length; j < len; j++){
33665                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
33666                     for(var i = 0; i < colCount; i++){
33667                         c = cs[i];
33668                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
33669                         p.id = c.id;
33670                         p.css = p.attr = "";
33671                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
33672                         if(p.value == undefined || p.value === "") {
33673                             p.value = "&#160;";
33674                         }
33675                         if(c.has_editor){
33676                             p.css += ' x-grid-editable-cell';
33677                         }
33678                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
33679                             p.css +=  ' x-grid-dirty-cell';
33680                         }
33681                         var markup = ct.apply(p);
33682                         if(!c.locked){
33683                             cb+= markup;
33684                         }else{
33685                             lcb+= markup;
33686                         }
33687                     }
33688                     var alt = [];
33689                     if(stripe && ((rowIndex+1) % 2 == 0)){
33690                         alt.push("x-grid-row-alt")
33691                     }
33692                     if(r.dirty){
33693                         alt.push(  " x-grid-dirty-row");
33694                     }
33695                     rp.cells = lcb;
33696                     if(this.getRowClass){
33697                         alt.push(this.getRowClass(r, rowIndex));
33698                     }
33699                     if (hasListener) {
33700                         rowcfg = {
33701                              
33702                             record: r,
33703                             rowIndex : rowIndex,
33704                             rowClass : ''
33705                         };
33706                         this.grid.fireEvent('rowclass', this, rowcfg);
33707                         alt.push(rowcfg.rowClass);
33708                     }
33709                     rp.alt = alt.join(" ");
33710                     lbuf+= rt.apply(rp);
33711                     rp.cells = cb;
33712                     buf+=  rt.apply(rp);
33713                 }
33714                 return [lbuf, buf];
33715             } :
33716             function(cs, rs, ds, startRow, colCount, stripe){
33717                 var ts = this.templates, ct = ts.cell, rt = ts.row;
33718                 // buffers
33719                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
33720                 var hasListener = this.grid.hasListener('rowclass');
33721  
33722                 var rowcfg = {};
33723                 for(var j = 0, len = rs.length; j < len; j++){
33724                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
33725                     for(var i = 0; i < colCount; i++){
33726                         c = cs[i];
33727                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
33728                         p.id = c.id;
33729                         p.css = p.attr = "";
33730                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
33731                         if(p.value == undefined || p.value === "") {
33732                             p.value = "&#160;";
33733                         }
33734                         //Roo.log(c);
33735                          if(c.has_editor){
33736                             p.css += ' x-grid-editable-cell';
33737                         }
33738                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
33739                             p.css += ' x-grid-dirty-cell' 
33740                         }
33741                         
33742                         var markup = ct.apply(p);
33743                         if(!c.locked){
33744                             cb[cb.length] = markup;
33745                         }else{
33746                             lcb[lcb.length] = markup;
33747                         }
33748                     }
33749                     var alt = [];
33750                     if(stripe && ((rowIndex+1) % 2 == 0)){
33751                         alt.push( "x-grid-row-alt");
33752                     }
33753                     if(r.dirty){
33754                         alt.push(" x-grid-dirty-row");
33755                     }
33756                     rp.cells = lcb;
33757                     if(this.getRowClass){
33758                         alt.push( this.getRowClass(r, rowIndex));
33759                     }
33760                     if (hasListener) {
33761                         rowcfg = {
33762                              
33763                             record: r,
33764                             rowIndex : rowIndex,
33765                             rowClass : ''
33766                         };
33767                         this.grid.fireEvent('rowclass', this, rowcfg);
33768                         alt.push(rowcfg.rowClass);
33769                     }
33770                     
33771                     rp.alt = alt.join(" ");
33772                     rp.cells = lcb.join("");
33773                     lbuf[lbuf.length] = rt.apply(rp);
33774                     rp.cells = cb.join("");
33775                     buf[buf.length] =  rt.apply(rp);
33776                 }
33777                 return [lbuf.join(""), buf.join("")];
33778             },
33779
33780     renderBody : function(){
33781         var markup = this.renderRows();
33782         var bt = this.templates.body;
33783         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
33784     },
33785
33786     /**
33787      * Refreshes the grid
33788      * @param {Boolean} headersToo
33789      */
33790     refresh : function(headersToo){
33791         this.fireEvent("beforerefresh", this);
33792         this.grid.stopEditing();
33793         var result = this.renderBody();
33794         this.lockedBody.update(result[0]);
33795         this.mainBody.update(result[1]);
33796         if(headersToo === true){
33797             this.updateHeaders();
33798             this.updateColumns();
33799             this.updateSplitters();
33800             this.updateHeaderSortState();
33801         }
33802         this.syncRowHeights();
33803         this.layout();
33804         this.fireEvent("refresh", this);
33805     },
33806
33807     handleColumnMove : function(cm, oldIndex, newIndex){
33808         this.indexMap = null;
33809         var s = this.getScrollState();
33810         this.refresh(true);
33811         this.restoreScroll(s);
33812         this.afterMove(newIndex);
33813     },
33814
33815     afterMove : function(colIndex){
33816         if(this.enableMoveAnim && Roo.enableFx){
33817             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
33818         }
33819         // if multisort - fix sortOrder, and reload..
33820         if (this.grid.dataSource.multiSort) {
33821             // the we can call sort again..
33822             var dm = this.grid.dataSource;
33823             var cm = this.grid.colModel;
33824             var so = [];
33825             for(var i = 0; i < cm.config.length; i++ ) {
33826                 
33827                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
33828                     continue; // dont' bother, it's not in sort list or being set.
33829                 }
33830                 
33831                 so.push(cm.config[i].dataIndex);
33832             };
33833             dm.sortOrder = so;
33834             dm.load(dm.lastOptions);
33835             
33836             
33837         }
33838         
33839     },
33840
33841     updateCell : function(dm, rowIndex, dataIndex){
33842         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
33843         if(typeof colIndex == "undefined"){ // not present in grid
33844             return;
33845         }
33846         var cm = this.grid.colModel;
33847         var cell = this.getCell(rowIndex, colIndex);
33848         var cellText = this.getCellText(rowIndex, colIndex);
33849
33850         var p = {
33851             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
33852             id : cm.getColumnId(colIndex),
33853             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
33854         };
33855         var renderer = cm.getRenderer(colIndex);
33856         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
33857         if(typeof val == "undefined" || val === "") {
33858             val = "&#160;";
33859         }
33860         cellText.innerHTML = val;
33861         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
33862         this.syncRowHeights(rowIndex, rowIndex);
33863     },
33864
33865     calcColumnWidth : function(colIndex, maxRowsToMeasure){
33866         var maxWidth = 0;
33867         if(this.grid.autoSizeHeaders){
33868             var h = this.getHeaderCellMeasure(colIndex);
33869             maxWidth = Math.max(maxWidth, h.scrollWidth);
33870         }
33871         var tb, index;
33872         if(this.cm.isLocked(colIndex)){
33873             tb = this.getLockedTable();
33874             index = colIndex;
33875         }else{
33876             tb = this.getBodyTable();
33877             index = colIndex - this.cm.getLockedCount();
33878         }
33879         if(tb && tb.rows){
33880             var rows = tb.rows;
33881             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
33882             for(var i = 0; i < stopIndex; i++){
33883                 var cell = rows[i].childNodes[index].firstChild;
33884                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
33885             }
33886         }
33887         return maxWidth + /*margin for error in IE*/ 5;
33888     },
33889     /**
33890      * Autofit a column to its content.
33891      * @param {Number} colIndex
33892      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
33893      */
33894      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
33895          if(this.cm.isHidden(colIndex)){
33896              return; // can't calc a hidden column
33897          }
33898         if(forceMinSize){
33899             var cid = this.cm.getColumnId(colIndex);
33900             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
33901            if(this.grid.autoSizeHeaders){
33902                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
33903            }
33904         }
33905         var newWidth = this.calcColumnWidth(colIndex);
33906         this.cm.setColumnWidth(colIndex,
33907             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
33908         if(!suppressEvent){
33909             this.grid.fireEvent("columnresize", colIndex, newWidth);
33910         }
33911     },
33912
33913     /**
33914      * Autofits all columns to their content and then expands to fit any extra space in the grid
33915      */
33916      autoSizeColumns : function(){
33917         var cm = this.grid.colModel;
33918         var colCount = cm.getColumnCount();
33919         for(var i = 0; i < colCount; i++){
33920             this.autoSizeColumn(i, true, true);
33921         }
33922         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
33923             this.fitColumns();
33924         }else{
33925             this.updateColumns();
33926             this.layout();
33927         }
33928     },
33929
33930     /**
33931      * Autofits all columns to the grid's width proportionate with their current size
33932      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
33933      */
33934     fitColumns : function(reserveScrollSpace){
33935         var cm = this.grid.colModel;
33936         var colCount = cm.getColumnCount();
33937         var cols = [];
33938         var width = 0;
33939         var i, w;
33940         for (i = 0; i < colCount; i++){
33941             if(!cm.isHidden(i) && !cm.isFixed(i)){
33942                 w = cm.getColumnWidth(i);
33943                 cols.push(i);
33944                 cols.push(w);
33945                 width += w;
33946             }
33947         }
33948         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
33949         if(reserveScrollSpace){
33950             avail -= 17;
33951         }
33952         var frac = (avail - cm.getTotalWidth())/width;
33953         while (cols.length){
33954             w = cols.pop();
33955             i = cols.pop();
33956             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
33957         }
33958         this.updateColumns();
33959         this.layout();
33960     },
33961
33962     onRowSelect : function(rowIndex){
33963         var row = this.getRowComposite(rowIndex);
33964         row.addClass("x-grid-row-selected");
33965     },
33966
33967     onRowDeselect : function(rowIndex){
33968         var row = this.getRowComposite(rowIndex);
33969         row.removeClass("x-grid-row-selected");
33970     },
33971
33972     onCellSelect : function(row, col){
33973         var cell = this.getCell(row, col);
33974         if(cell){
33975             Roo.fly(cell).addClass("x-grid-cell-selected");
33976         }
33977     },
33978
33979     onCellDeselect : function(row, col){
33980         var cell = this.getCell(row, col);
33981         if(cell){
33982             Roo.fly(cell).removeClass("x-grid-cell-selected");
33983         }
33984     },
33985
33986     updateHeaderSortState : function(){
33987         
33988         // sort state can be single { field: xxx, direction : yyy}
33989         // or   { xxx=>ASC , yyy : DESC ..... }
33990         
33991         var mstate = {};
33992         if (!this.ds.multiSort) { 
33993             var state = this.ds.getSortState();
33994             if(!state){
33995                 return;
33996             }
33997             mstate[state.field] = state.direction;
33998             // FIXME... - this is not used here.. but might be elsewhere..
33999             this.sortState = state;
34000             
34001         } else {
34002             mstate = this.ds.sortToggle;
34003         }
34004         //remove existing sort classes..
34005         
34006         var sc = this.sortClasses;
34007         var hds = this.el.select(this.headerSelector).removeClass(sc);
34008         
34009         for(var f in mstate) {
34010         
34011             var sortColumn = this.cm.findColumnIndex(f);
34012             
34013             if(sortColumn != -1){
34014                 var sortDir = mstate[f];        
34015                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
34016             }
34017         }
34018         
34019          
34020         
34021     },
34022
34023
34024     handleHeaderClick : function(g, index,e){
34025         
34026         Roo.log("header click");
34027         
34028         if (Roo.isTouch) {
34029             // touch events on header are handled by context
34030             this.handleHdCtx(g,index,e);
34031             return;
34032         }
34033         
34034         
34035         if(this.headersDisabled){
34036             return;
34037         }
34038         var dm = g.dataSource, cm = g.colModel;
34039         if(!cm.isSortable(index)){
34040             return;
34041         }
34042         g.stopEditing();
34043         
34044         if (dm.multiSort) {
34045             // update the sortOrder
34046             var so = [];
34047             for(var i = 0; i < cm.config.length; i++ ) {
34048                 
34049                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
34050                     continue; // dont' bother, it's not in sort list or being set.
34051                 }
34052                 
34053                 so.push(cm.config[i].dataIndex);
34054             };
34055             dm.sortOrder = so;
34056         }
34057         
34058         
34059         dm.sort(cm.getDataIndex(index));
34060     },
34061
34062
34063     destroy : function(){
34064         if(this.colMenu){
34065             this.colMenu.removeAll();
34066             Roo.menu.MenuMgr.unregister(this.colMenu);
34067             this.colMenu.getEl().remove();
34068             delete this.colMenu;
34069         }
34070         if(this.hmenu){
34071             this.hmenu.removeAll();
34072             Roo.menu.MenuMgr.unregister(this.hmenu);
34073             this.hmenu.getEl().remove();
34074             delete this.hmenu;
34075         }
34076         if(this.grid.enableColumnMove){
34077             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34078             if(dds){
34079                 for(var dd in dds){
34080                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
34081                         var elid = dds[dd].dragElId;
34082                         dds[dd].unreg();
34083                         Roo.get(elid).remove();
34084                     } else if(dds[dd].config.isTarget){
34085                         dds[dd].proxyTop.remove();
34086                         dds[dd].proxyBottom.remove();
34087                         dds[dd].unreg();
34088                     }
34089                     if(Roo.dd.DDM.locationCache[dd]){
34090                         delete Roo.dd.DDM.locationCache[dd];
34091                     }
34092                 }
34093                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34094             }
34095         }
34096         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
34097         this.bind(null, null);
34098         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
34099     },
34100
34101     handleLockChange : function(){
34102         this.refresh(true);
34103     },
34104
34105     onDenyColumnLock : function(){
34106
34107     },
34108
34109     onDenyColumnHide : function(){
34110
34111     },
34112
34113     handleHdMenuClick : function(item){
34114         var index = this.hdCtxIndex;
34115         var cm = this.cm, ds = this.ds;
34116         switch(item.id){
34117             case "asc":
34118                 ds.sort(cm.getDataIndex(index), "ASC");
34119                 break;
34120             case "desc":
34121                 ds.sort(cm.getDataIndex(index), "DESC");
34122                 break;
34123             case "lock":
34124                 var lc = cm.getLockedCount();
34125                 if(cm.getColumnCount(true) <= lc+1){
34126                     this.onDenyColumnLock();
34127                     return;
34128                 }
34129                 if(lc != index){
34130                     cm.setLocked(index, true, true);
34131                     cm.moveColumn(index, lc);
34132                     this.grid.fireEvent("columnmove", index, lc);
34133                 }else{
34134                     cm.setLocked(index, true);
34135                 }
34136             break;
34137             case "unlock":
34138                 var lc = cm.getLockedCount();
34139                 if((lc-1) != index){
34140                     cm.setLocked(index, false, true);
34141                     cm.moveColumn(index, lc-1);
34142                     this.grid.fireEvent("columnmove", index, lc-1);
34143                 }else{
34144                     cm.setLocked(index, false);
34145                 }
34146             break;
34147             case 'wider': // used to expand cols on touch..
34148             case 'narrow':
34149                 var cw = cm.getColumnWidth(index);
34150                 cw += (item.id == 'wider' ? 1 : -1) * 50;
34151                 cw = Math.max(0, cw);
34152                 cw = Math.min(cw,4000);
34153                 cm.setColumnWidth(index, cw);
34154                 break;
34155                 
34156             default:
34157                 index = cm.getIndexById(item.id.substr(4));
34158                 if(index != -1){
34159                     if(item.checked && cm.getColumnCount(true) <= 1){
34160                         this.onDenyColumnHide();
34161                         return false;
34162                     }
34163                     cm.setHidden(index, item.checked);
34164                 }
34165         }
34166         return true;
34167     },
34168
34169     beforeColMenuShow : function(){
34170         var cm = this.cm,  colCount = cm.getColumnCount();
34171         this.colMenu.removeAll();
34172         for(var i = 0; i < colCount; i++){
34173             this.colMenu.add(new Roo.menu.CheckItem({
34174                 id: "col-"+cm.getColumnId(i),
34175                 text: cm.getColumnHeader(i),
34176                 checked: !cm.isHidden(i),
34177                 hideOnClick:false
34178             }));
34179         }
34180     },
34181
34182     handleHdCtx : function(g, index, e){
34183         e.stopEvent();
34184         var hd = this.getHeaderCell(index);
34185         this.hdCtxIndex = index;
34186         var ms = this.hmenu.items, cm = this.cm;
34187         ms.get("asc").setDisabled(!cm.isSortable(index));
34188         ms.get("desc").setDisabled(!cm.isSortable(index));
34189         if(this.grid.enableColLock !== false){
34190             ms.get("lock").setDisabled(cm.isLocked(index));
34191             ms.get("unlock").setDisabled(!cm.isLocked(index));
34192         }
34193         this.hmenu.show(hd, "tl-bl");
34194     },
34195
34196     handleHdOver : function(e){
34197         var hd = this.findHeaderCell(e.getTarget());
34198         if(hd && !this.headersDisabled){
34199             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
34200                this.fly(hd).addClass("x-grid-hd-over");
34201             }
34202         }
34203     },
34204
34205     handleHdOut : function(e){
34206         var hd = this.findHeaderCell(e.getTarget());
34207         if(hd){
34208             this.fly(hd).removeClass("x-grid-hd-over");
34209         }
34210     },
34211
34212     handleSplitDblClick : function(e, t){
34213         var i = this.getCellIndex(t);
34214         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
34215             this.autoSizeColumn(i, true);
34216             this.layout();
34217         }
34218     },
34219
34220     render : function(){
34221
34222         var cm = this.cm;
34223         var colCount = cm.getColumnCount();
34224
34225         if(this.grid.monitorWindowResize === true){
34226             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34227         }
34228         var header = this.renderHeaders();
34229         var body = this.templates.body.apply({rows:""});
34230         var html = this.templates.master.apply({
34231             lockedBody: body,
34232             body: body,
34233             lockedHeader: header[0],
34234             header: header[1]
34235         });
34236
34237         //this.updateColumns();
34238
34239         this.grid.getGridEl().dom.innerHTML = html;
34240
34241         this.initElements();
34242         
34243         // a kludge to fix the random scolling effect in webkit
34244         this.el.on("scroll", function() {
34245             this.el.dom.scrollTop=0; // hopefully not recursive..
34246         },this);
34247
34248         this.scroller.on("scroll", this.handleScroll, this);
34249         this.lockedBody.on("mousewheel", this.handleWheel, this);
34250         this.mainBody.on("mousewheel", this.handleWheel, this);
34251
34252         this.mainHd.on("mouseover", this.handleHdOver, this);
34253         this.mainHd.on("mouseout", this.handleHdOut, this);
34254         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
34255                 {delegate: "."+this.splitClass});
34256
34257         this.lockedHd.on("mouseover", this.handleHdOver, this);
34258         this.lockedHd.on("mouseout", this.handleHdOut, this);
34259         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
34260                 {delegate: "."+this.splitClass});
34261
34262         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
34263             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34264         }
34265
34266         this.updateSplitters();
34267
34268         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
34269             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34270             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34271         }
34272
34273         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
34274             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
34275             this.hmenu.add(
34276                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
34277                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
34278             );
34279             if(this.grid.enableColLock !== false){
34280                 this.hmenu.add('-',
34281                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
34282                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
34283                 );
34284             }
34285             if (Roo.isTouch) {
34286                  this.hmenu.add('-',
34287                     {id:"wider", text: this.columnsWiderText},
34288                     {id:"narrow", text: this.columnsNarrowText }
34289                 );
34290                 
34291                  
34292             }
34293             
34294             if(this.grid.enableColumnHide !== false){
34295
34296                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
34297                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
34298                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
34299
34300                 this.hmenu.add('-',
34301                     {id:"columns", text: this.columnsText, menu: this.colMenu}
34302                 );
34303             }
34304             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
34305
34306             this.grid.on("headercontextmenu", this.handleHdCtx, this);
34307         }
34308
34309         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
34310             this.dd = new Roo.grid.GridDragZone(this.grid, {
34311                 ddGroup : this.grid.ddGroup || 'GridDD'
34312             });
34313             
34314         }
34315
34316         /*
34317         for(var i = 0; i < colCount; i++){
34318             if(cm.isHidden(i)){
34319                 this.hideColumn(i);
34320             }
34321             if(cm.config[i].align){
34322                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
34323                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
34324             }
34325         }*/
34326         
34327         this.updateHeaderSortState();
34328
34329         this.beforeInitialResize();
34330         this.layout(true);
34331
34332         // two part rendering gives faster view to the user
34333         this.renderPhase2.defer(1, this);
34334     },
34335
34336     renderPhase2 : function(){
34337         // render the rows now
34338         this.refresh();
34339         if(this.grid.autoSizeColumns){
34340             this.autoSizeColumns();
34341         }
34342     },
34343
34344     beforeInitialResize : function(){
34345
34346     },
34347
34348     onColumnSplitterMoved : function(i, w){
34349         this.userResized = true;
34350         var cm = this.grid.colModel;
34351         cm.setColumnWidth(i, w, true);
34352         var cid = cm.getColumnId(i);
34353         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34354         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34355         this.updateSplitters();
34356         this.layout();
34357         this.grid.fireEvent("columnresize", i, w);
34358     },
34359
34360     syncRowHeights : function(startIndex, endIndex){
34361         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
34362             startIndex = startIndex || 0;
34363             var mrows = this.getBodyTable().rows;
34364             var lrows = this.getLockedTable().rows;
34365             var len = mrows.length-1;
34366             endIndex = Math.min(endIndex || len, len);
34367             for(var i = startIndex; i <= endIndex; i++){
34368                 var m = mrows[i], l = lrows[i];
34369                 var h = Math.max(m.offsetHeight, l.offsetHeight);
34370                 m.style.height = l.style.height = h + "px";
34371             }
34372         }
34373     },
34374
34375     layout : function(initialRender, is2ndPass){
34376         var g = this.grid;
34377         var auto = g.autoHeight;
34378         var scrollOffset = 16;
34379         var c = g.getGridEl(), cm = this.cm,
34380                 expandCol = g.autoExpandColumn,
34381                 gv = this;
34382         //c.beginMeasure();
34383
34384         if(!c.dom.offsetWidth){ // display:none?
34385             if(initialRender){
34386                 this.lockedWrap.show();
34387                 this.mainWrap.show();
34388             }
34389             return;
34390         }
34391
34392         var hasLock = this.cm.isLocked(0);
34393
34394         var tbh = this.headerPanel.getHeight();
34395         var bbh = this.footerPanel.getHeight();
34396
34397         if(auto){
34398             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
34399             var newHeight = ch + c.getBorderWidth("tb");
34400             if(g.maxHeight){
34401                 newHeight = Math.min(g.maxHeight, newHeight);
34402             }
34403             c.setHeight(newHeight);
34404         }
34405
34406         if(g.autoWidth){
34407             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
34408         }
34409
34410         var s = this.scroller;
34411
34412         var csize = c.getSize(true);
34413
34414         this.el.setSize(csize.width, csize.height);
34415
34416         this.headerPanel.setWidth(csize.width);
34417         this.footerPanel.setWidth(csize.width);
34418
34419         var hdHeight = this.mainHd.getHeight();
34420         var vw = csize.width;
34421         var vh = csize.height - (tbh + bbh);
34422
34423         s.setSize(vw, vh);
34424
34425         var bt = this.getBodyTable();
34426         
34427         if(cm.getLockedCount() == cm.config.length){
34428             bt = this.getLockedTable();
34429         }
34430         
34431         var ltWidth = hasLock ?
34432                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
34433
34434         var scrollHeight = bt.offsetHeight;
34435         var scrollWidth = ltWidth + bt.offsetWidth;
34436         var vscroll = false, hscroll = false;
34437
34438         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
34439
34440         var lw = this.lockedWrap, mw = this.mainWrap;
34441         var lb = this.lockedBody, mb = this.mainBody;
34442
34443         setTimeout(function(){
34444             var t = s.dom.offsetTop;
34445             var w = s.dom.clientWidth,
34446                 h = s.dom.clientHeight;
34447
34448             lw.setTop(t);
34449             lw.setSize(ltWidth, h);
34450
34451             mw.setLeftTop(ltWidth, t);
34452             mw.setSize(w-ltWidth, h);
34453
34454             lb.setHeight(h-hdHeight);
34455             mb.setHeight(h-hdHeight);
34456
34457             if(is2ndPass !== true && !gv.userResized && expandCol){
34458                 // high speed resize without full column calculation
34459                 
34460                 var ci = cm.getIndexById(expandCol);
34461                 if (ci < 0) {
34462                     ci = cm.findColumnIndex(expandCol);
34463                 }
34464                 ci = Math.max(0, ci); // make sure it's got at least the first col.
34465                 var expandId = cm.getColumnId(ci);
34466                 var  tw = cm.getTotalWidth(false);
34467                 var currentWidth = cm.getColumnWidth(ci);
34468                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
34469                 if(currentWidth != cw){
34470                     cm.setColumnWidth(ci, cw, true);
34471                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34472                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34473                     gv.updateSplitters();
34474                     gv.layout(false, true);
34475                 }
34476             }
34477
34478             if(initialRender){
34479                 lw.show();
34480                 mw.show();
34481             }
34482             //c.endMeasure();
34483         }, 10);
34484     },
34485
34486     onWindowResize : function(){
34487         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
34488             return;
34489         }
34490         this.layout();
34491     },
34492
34493     appendFooter : function(parentEl){
34494         return null;
34495     },
34496
34497     sortAscText : "Sort Ascending",
34498     sortDescText : "Sort Descending",
34499     lockText : "Lock Column",
34500     unlockText : "Unlock Column",
34501     columnsText : "Columns",
34502  
34503     columnsWiderText : "Wider",
34504     columnsNarrowText : "Thinner"
34505 });
34506
34507
34508 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
34509     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
34510     this.proxy.el.addClass('x-grid3-col-dd');
34511 };
34512
34513 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
34514     handleMouseDown : function(e){
34515
34516     },
34517
34518     callHandleMouseDown : function(e){
34519         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
34520     }
34521 });
34522 /*
34523  * Based on:
34524  * Ext JS Library 1.1.1
34525  * Copyright(c) 2006-2007, Ext JS, LLC.
34526  *
34527  * Originally Released Under LGPL - original licence link has changed is not relivant.
34528  *
34529  * Fork - LGPL
34530  * <script type="text/javascript">
34531  */
34532  
34533 // private
34534 // This is a support class used internally by the Grid components
34535 Roo.grid.SplitDragZone = function(grid, hd, hd2){
34536     this.grid = grid;
34537     this.view = grid.getView();
34538     this.proxy = this.view.resizeProxy;
34539     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
34540         "gridSplitters" + this.grid.getGridEl().id, {
34541         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
34542     });
34543     this.setHandleElId(Roo.id(hd));
34544     this.setOuterHandleElId(Roo.id(hd2));
34545     this.scroll = false;
34546 };
34547 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
34548     fly: Roo.Element.fly,
34549
34550     b4StartDrag : function(x, y){
34551         this.view.headersDisabled = true;
34552         this.proxy.setHeight(this.view.mainWrap.getHeight());
34553         var w = this.cm.getColumnWidth(this.cellIndex);
34554         var minw = Math.max(w-this.grid.minColumnWidth, 0);
34555         this.resetConstraints();
34556         this.setXConstraint(minw, 1000);
34557         this.setYConstraint(0, 0);
34558         this.minX = x - minw;
34559         this.maxX = x + 1000;
34560         this.startPos = x;
34561         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
34562     },
34563
34564
34565     handleMouseDown : function(e){
34566         ev = Roo.EventObject.setEvent(e);
34567         var t = this.fly(ev.getTarget());
34568         if(t.hasClass("x-grid-split")){
34569             this.cellIndex = this.view.getCellIndex(t.dom);
34570             this.split = t.dom;
34571             this.cm = this.grid.colModel;
34572             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
34573                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
34574             }
34575         }
34576     },
34577
34578     endDrag : function(e){
34579         this.view.headersDisabled = false;
34580         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
34581         var diff = endX - this.startPos;
34582         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
34583     },
34584
34585     autoOffset : function(){
34586         this.setDelta(0,0);
34587     }
34588 });/*
34589  * Based on:
34590  * Ext JS Library 1.1.1
34591  * Copyright(c) 2006-2007, Ext JS, LLC.
34592  *
34593  * Originally Released Under LGPL - original licence link has changed is not relivant.
34594  *
34595  * Fork - LGPL
34596  * <script type="text/javascript">
34597  */
34598  
34599 // private
34600 // This is a support class used internally by the Grid components
34601 Roo.grid.GridDragZone = function(grid, config){
34602     this.view = grid.getView();
34603     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
34604     if(this.view.lockedBody){
34605         this.setHandleElId(Roo.id(this.view.mainBody.dom));
34606         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
34607     }
34608     this.scroll = false;
34609     this.grid = grid;
34610     this.ddel = document.createElement('div');
34611     this.ddel.className = 'x-grid-dd-wrap';
34612 };
34613
34614 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
34615     ddGroup : "GridDD",
34616
34617     getDragData : function(e){
34618         var t = Roo.lib.Event.getTarget(e);
34619         var rowIndex = this.view.findRowIndex(t);
34620         var sm = this.grid.selModel;
34621             
34622         //Roo.log(rowIndex);
34623         
34624         if (sm.getSelectedCell) {
34625             // cell selection..
34626             if (!sm.getSelectedCell()) {
34627                 return false;
34628             }
34629             if (rowIndex != sm.getSelectedCell()[0]) {
34630                 return false;
34631             }
34632         
34633         }
34634         
34635         if(rowIndex !== false){
34636             
34637             // if editorgrid.. 
34638             
34639             
34640             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
34641                
34642             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
34643               //  
34644             //}
34645             if (e.hasModifier()){
34646                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
34647             }
34648             
34649             Roo.log("getDragData");
34650             
34651             return {
34652                 grid: this.grid,
34653                 ddel: this.ddel,
34654                 rowIndex: rowIndex,
34655                 selections:sm.getSelections ? sm.getSelections() : (
34656                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
34657                 )
34658             };
34659         }
34660         return false;
34661     },
34662
34663     onInitDrag : function(e){
34664         var data = this.dragData;
34665         this.ddel.innerHTML = this.grid.getDragDropText();
34666         this.proxy.update(this.ddel);
34667         // fire start drag?
34668     },
34669
34670     afterRepair : function(){
34671         this.dragging = false;
34672     },
34673
34674     getRepairXY : function(e, data){
34675         return false;
34676     },
34677
34678     onEndDrag : function(data, e){
34679         // fire end drag?
34680     },
34681
34682     onValidDrop : function(dd, e, id){
34683         // fire drag drop?
34684         this.hideProxy();
34685     },
34686
34687     beforeInvalidDrop : function(e, id){
34688
34689     }
34690 });/*
34691  * Based on:
34692  * Ext JS Library 1.1.1
34693  * Copyright(c) 2006-2007, Ext JS, LLC.
34694  *
34695  * Originally Released Under LGPL - original licence link has changed is not relivant.
34696  *
34697  * Fork - LGPL
34698  * <script type="text/javascript">
34699  */
34700  
34701
34702 /**
34703  * @class Roo.grid.ColumnModel
34704  * @extends Roo.util.Observable
34705  * This is the default implementation of a ColumnModel used by the Grid. It defines
34706  * the columns in the grid.
34707  * <br>Usage:<br>
34708  <pre><code>
34709  var colModel = new Roo.grid.ColumnModel([
34710         {header: "Ticker", width: 60, sortable: true, locked: true},
34711         {header: "Company Name", width: 150, sortable: true},
34712         {header: "Market Cap.", width: 100, sortable: true},
34713         {header: "$ Sales", width: 100, sortable: true, renderer: money},
34714         {header: "Employees", width: 100, sortable: true, resizable: false}
34715  ]);
34716  </code></pre>
34717  * <p>
34718  
34719  * The config options listed for this class are options which may appear in each
34720  * individual column definition.
34721  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
34722  * @constructor
34723  * @param {Object} config An Array of column config objects. See this class's
34724  * config objects for details.
34725 */
34726 Roo.grid.ColumnModel = function(config){
34727         /**
34728      * The config passed into the constructor
34729      */
34730     this.config = config;
34731     this.lookup = {};
34732
34733     // if no id, create one
34734     // if the column does not have a dataIndex mapping,
34735     // map it to the order it is in the config
34736     for(var i = 0, len = config.length; i < len; i++){
34737         var c = config[i];
34738         if(typeof c.dataIndex == "undefined"){
34739             c.dataIndex = i;
34740         }
34741         if(typeof c.renderer == "string"){
34742             c.renderer = Roo.util.Format[c.renderer];
34743         }
34744         if(typeof c.id == "undefined"){
34745             c.id = Roo.id();
34746         }
34747         if(c.editor && c.editor.xtype){
34748             c.editor  = Roo.factory(c.editor, Roo.grid);
34749         }
34750         if(c.editor && c.editor.isFormField){
34751             c.editor = new Roo.grid.GridEditor(c.editor);
34752         }
34753         this.lookup[c.id] = c;
34754     }
34755
34756     /**
34757      * The width of columns which have no width specified (defaults to 100)
34758      * @type Number
34759      */
34760     this.defaultWidth = 100;
34761
34762     /**
34763      * Default sortable of columns which have no sortable specified (defaults to false)
34764      * @type Boolean
34765      */
34766     this.defaultSortable = false;
34767
34768     this.addEvents({
34769         /**
34770              * @event widthchange
34771              * Fires when the width of a column changes.
34772              * @param {ColumnModel} this
34773              * @param {Number} columnIndex The column index
34774              * @param {Number} newWidth The new width
34775              */
34776             "widthchange": true,
34777         /**
34778              * @event headerchange
34779              * Fires when the text of a header changes.
34780              * @param {ColumnModel} this
34781              * @param {Number} columnIndex The column index
34782              * @param {Number} newText The new header text
34783              */
34784             "headerchange": true,
34785         /**
34786              * @event hiddenchange
34787              * Fires when a column is hidden or "unhidden".
34788              * @param {ColumnModel} this
34789              * @param {Number} columnIndex The column index
34790              * @param {Boolean} hidden true if hidden, false otherwise
34791              */
34792             "hiddenchange": true,
34793             /**
34794          * @event columnmoved
34795          * Fires when a column is moved.
34796          * @param {ColumnModel} this
34797          * @param {Number} oldIndex
34798          * @param {Number} newIndex
34799          */
34800         "columnmoved" : true,
34801         /**
34802          * @event columlockchange
34803          * Fires when a column's locked state is changed
34804          * @param {ColumnModel} this
34805          * @param {Number} colIndex
34806          * @param {Boolean} locked true if locked
34807          */
34808         "columnlockchange" : true
34809     });
34810     Roo.grid.ColumnModel.superclass.constructor.call(this);
34811 };
34812 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
34813     /**
34814      * @cfg {String} header The header text to display in the Grid view.
34815      */
34816     /**
34817      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
34818      * {@link Roo.data.Record} definition from which to draw the column's value. If not
34819      * specified, the column's index is used as an index into the Record's data Array.
34820      */
34821     /**
34822      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
34823      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
34824      */
34825     /**
34826      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
34827      * Defaults to the value of the {@link #defaultSortable} property.
34828      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
34829      */
34830     /**
34831      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
34832      */
34833     /**
34834      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
34835      */
34836     /**
34837      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
34838      */
34839     /**
34840      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
34841      */
34842     /**
34843      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
34844      * given the cell's data value. See {@link #setRenderer}. If not specified, the
34845      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
34846      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
34847      */
34848        /**
34849      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
34850      */
34851     /**
34852      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
34853      */
34854     /**
34855      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
34856      */
34857     /**
34858      * @cfg {String} cursor (Optional)
34859      */
34860     /**
34861      * @cfg {String} tooltip (Optional)
34862      */
34863     /**
34864      * @cfg {Number} xs (Optional)
34865      */
34866     /**
34867      * @cfg {Number} sm (Optional)
34868      */
34869     /**
34870      * @cfg {Number} md (Optional)
34871      */
34872     /**
34873      * @cfg {Number} lg (Optional)
34874      */
34875     /**
34876      * Returns the id of the column at the specified index.
34877      * @param {Number} index The column index
34878      * @return {String} the id
34879      */
34880     getColumnId : function(index){
34881         return this.config[index].id;
34882     },
34883
34884     /**
34885      * Returns the column for a specified id.
34886      * @param {String} id The column id
34887      * @return {Object} the column
34888      */
34889     getColumnById : function(id){
34890         return this.lookup[id];
34891     },
34892
34893     
34894     /**
34895      * Returns the column for a specified dataIndex.
34896      * @param {String} dataIndex The column dataIndex
34897      * @return {Object|Boolean} the column or false if not found
34898      */
34899     getColumnByDataIndex: function(dataIndex){
34900         var index = this.findColumnIndex(dataIndex);
34901         return index > -1 ? this.config[index] : false;
34902     },
34903     
34904     /**
34905      * Returns the index for a specified column id.
34906      * @param {String} id The column id
34907      * @return {Number} the index, or -1 if not found
34908      */
34909     getIndexById : function(id){
34910         for(var i = 0, len = this.config.length; i < len; i++){
34911             if(this.config[i].id == id){
34912                 return i;
34913             }
34914         }
34915         return -1;
34916     },
34917     
34918     /**
34919      * Returns the index for a specified column dataIndex.
34920      * @param {String} dataIndex The column dataIndex
34921      * @return {Number} the index, or -1 if not found
34922      */
34923     
34924     findColumnIndex : function(dataIndex){
34925         for(var i = 0, len = this.config.length; i < len; i++){
34926             if(this.config[i].dataIndex == dataIndex){
34927                 return i;
34928             }
34929         }
34930         return -1;
34931     },
34932     
34933     
34934     moveColumn : function(oldIndex, newIndex){
34935         var c = this.config[oldIndex];
34936         this.config.splice(oldIndex, 1);
34937         this.config.splice(newIndex, 0, c);
34938         this.dataMap = null;
34939         this.fireEvent("columnmoved", this, oldIndex, newIndex);
34940     },
34941
34942     isLocked : function(colIndex){
34943         return this.config[colIndex].locked === true;
34944     },
34945
34946     setLocked : function(colIndex, value, suppressEvent){
34947         if(this.isLocked(colIndex) == value){
34948             return;
34949         }
34950         this.config[colIndex].locked = value;
34951         if(!suppressEvent){
34952             this.fireEvent("columnlockchange", this, colIndex, value);
34953         }
34954     },
34955
34956     getTotalLockedWidth : function(){
34957         var totalWidth = 0;
34958         for(var i = 0; i < this.config.length; i++){
34959             if(this.isLocked(i) && !this.isHidden(i)){
34960                 this.totalWidth += this.getColumnWidth(i);
34961             }
34962         }
34963         return totalWidth;
34964     },
34965
34966     getLockedCount : function(){
34967         for(var i = 0, len = this.config.length; i < len; i++){
34968             if(!this.isLocked(i)){
34969                 return i;
34970             }
34971         }
34972         
34973         return this.config.length;
34974     },
34975
34976     /**
34977      * Returns the number of columns.
34978      * @return {Number}
34979      */
34980     getColumnCount : function(visibleOnly){
34981         if(visibleOnly === true){
34982             var c = 0;
34983             for(var i = 0, len = this.config.length; i < len; i++){
34984                 if(!this.isHidden(i)){
34985                     c++;
34986                 }
34987             }
34988             return c;
34989         }
34990         return this.config.length;
34991     },
34992
34993     /**
34994      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
34995      * @param {Function} fn
34996      * @param {Object} scope (optional)
34997      * @return {Array} result
34998      */
34999     getColumnsBy : function(fn, scope){
35000         var r = [];
35001         for(var i = 0, len = this.config.length; i < len; i++){
35002             var c = this.config[i];
35003             if(fn.call(scope||this, c, i) === true){
35004                 r[r.length] = c;
35005             }
35006         }
35007         return r;
35008     },
35009
35010     /**
35011      * Returns true if the specified column is sortable.
35012      * @param {Number} col The column index
35013      * @return {Boolean}
35014      */
35015     isSortable : function(col){
35016         if(typeof this.config[col].sortable == "undefined"){
35017             return this.defaultSortable;
35018         }
35019         return this.config[col].sortable;
35020     },
35021
35022     /**
35023      * Returns the rendering (formatting) function defined for the column.
35024      * @param {Number} col The column index.
35025      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
35026      */
35027     getRenderer : function(col){
35028         if(!this.config[col].renderer){
35029             return Roo.grid.ColumnModel.defaultRenderer;
35030         }
35031         return this.config[col].renderer;
35032     },
35033
35034     /**
35035      * Sets the rendering (formatting) function for a column.
35036      * @param {Number} col The column index
35037      * @param {Function} fn The function to use to process the cell's raw data
35038      * to return HTML markup for the grid view. The render function is called with
35039      * the following parameters:<ul>
35040      * <li>Data value.</li>
35041      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
35042      * <li>css A CSS style string to apply to the table cell.</li>
35043      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
35044      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
35045      * <li>Row index</li>
35046      * <li>Column index</li>
35047      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
35048      */
35049     setRenderer : function(col, fn){
35050         this.config[col].renderer = fn;
35051     },
35052
35053     /**
35054      * Returns the width for the specified column.
35055      * @param {Number} col The column index
35056      * @return {Number}
35057      */
35058     getColumnWidth : function(col){
35059         return this.config[col].width * 1 || this.defaultWidth;
35060     },
35061
35062     /**
35063      * Sets the width for a column.
35064      * @param {Number} col The column index
35065      * @param {Number} width The new width
35066      */
35067     setColumnWidth : function(col, width, suppressEvent){
35068         this.config[col].width = width;
35069         this.totalWidth = null;
35070         if(!suppressEvent){
35071              this.fireEvent("widthchange", this, col, width);
35072         }
35073     },
35074
35075     /**
35076      * Returns the total width of all columns.
35077      * @param {Boolean} includeHidden True to include hidden column widths
35078      * @return {Number}
35079      */
35080     getTotalWidth : function(includeHidden){
35081         if(!this.totalWidth){
35082             this.totalWidth = 0;
35083             for(var i = 0, len = this.config.length; i < len; i++){
35084                 if(includeHidden || !this.isHidden(i)){
35085                     this.totalWidth += this.getColumnWidth(i);
35086                 }
35087             }
35088         }
35089         return this.totalWidth;
35090     },
35091
35092     /**
35093      * Returns the header for the specified column.
35094      * @param {Number} col The column index
35095      * @return {String}
35096      */
35097     getColumnHeader : function(col){
35098         return this.config[col].header;
35099     },
35100
35101     /**
35102      * Sets the header for a column.
35103      * @param {Number} col The column index
35104      * @param {String} header The new header
35105      */
35106     setColumnHeader : function(col, header){
35107         this.config[col].header = header;
35108         this.fireEvent("headerchange", this, col, header);
35109     },
35110
35111     /**
35112      * Returns the tooltip for the specified column.
35113      * @param {Number} col The column index
35114      * @return {String}
35115      */
35116     getColumnTooltip : function(col){
35117             return this.config[col].tooltip;
35118     },
35119     /**
35120      * Sets the tooltip for a column.
35121      * @param {Number} col The column index
35122      * @param {String} tooltip The new tooltip
35123      */
35124     setColumnTooltip : function(col, tooltip){
35125             this.config[col].tooltip = tooltip;
35126     },
35127
35128     /**
35129      * Returns the dataIndex for the specified column.
35130      * @param {Number} col The column index
35131      * @return {Number}
35132      */
35133     getDataIndex : function(col){
35134         return this.config[col].dataIndex;
35135     },
35136
35137     /**
35138      * Sets the dataIndex for a column.
35139      * @param {Number} col The column index
35140      * @param {Number} dataIndex The new dataIndex
35141      */
35142     setDataIndex : function(col, dataIndex){
35143         this.config[col].dataIndex = dataIndex;
35144     },
35145
35146     
35147     
35148     /**
35149      * Returns true if the cell is editable.
35150      * @param {Number} colIndex The column index
35151      * @param {Number} rowIndex The row index - this is nto actually used..?
35152      * @return {Boolean}
35153      */
35154     isCellEditable : function(colIndex, rowIndex){
35155         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
35156     },
35157
35158     /**
35159      * Returns the editor defined for the cell/column.
35160      * return false or null to disable editing.
35161      * @param {Number} colIndex The column index
35162      * @param {Number} rowIndex The row index
35163      * @return {Object}
35164      */
35165     getCellEditor : function(colIndex, rowIndex){
35166         return this.config[colIndex].editor;
35167     },
35168
35169     /**
35170      * Sets if a column is editable.
35171      * @param {Number} col The column index
35172      * @param {Boolean} editable True if the column is editable
35173      */
35174     setEditable : function(col, editable){
35175         this.config[col].editable = editable;
35176     },
35177
35178
35179     /**
35180      * Returns true if the column is hidden.
35181      * @param {Number} colIndex The column index
35182      * @return {Boolean}
35183      */
35184     isHidden : function(colIndex){
35185         return this.config[colIndex].hidden;
35186     },
35187
35188
35189     /**
35190      * Returns true if the column width cannot be changed
35191      */
35192     isFixed : function(colIndex){
35193         return this.config[colIndex].fixed;
35194     },
35195
35196     /**
35197      * Returns true if the column can be resized
35198      * @return {Boolean}
35199      */
35200     isResizable : function(colIndex){
35201         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
35202     },
35203     /**
35204      * Sets if a column is hidden.
35205      * @param {Number} colIndex The column index
35206      * @param {Boolean} hidden True if the column is hidden
35207      */
35208     setHidden : function(colIndex, hidden){
35209         this.config[colIndex].hidden = hidden;
35210         this.totalWidth = null;
35211         this.fireEvent("hiddenchange", this, colIndex, hidden);
35212     },
35213
35214     /**
35215      * Sets the editor for a column.
35216      * @param {Number} col The column index
35217      * @param {Object} editor The editor object
35218      */
35219     setEditor : function(col, editor){
35220         this.config[col].editor = editor;
35221     }
35222 });
35223
35224 Roo.grid.ColumnModel.defaultRenderer = function(value)
35225 {
35226     if(typeof value == "object") {
35227         return value;
35228     }
35229         if(typeof value == "string" && value.length < 1){
35230             return "&#160;";
35231         }
35232     
35233         return String.format("{0}", value);
35234 };
35235
35236 // Alias for backwards compatibility
35237 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
35238 /*
35239  * Based on:
35240  * Ext JS Library 1.1.1
35241  * Copyright(c) 2006-2007, Ext JS, LLC.
35242  *
35243  * Originally Released Under LGPL - original licence link has changed is not relivant.
35244  *
35245  * Fork - LGPL
35246  * <script type="text/javascript">
35247  */
35248
35249 /**
35250  * @class Roo.grid.AbstractSelectionModel
35251  * @extends Roo.util.Observable
35252  * Abstract base class for grid SelectionModels.  It provides the interface that should be
35253  * implemented by descendant classes.  This class should not be directly instantiated.
35254  * @constructor
35255  */
35256 Roo.grid.AbstractSelectionModel = function(){
35257     this.locked = false;
35258     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
35259 };
35260
35261 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
35262     /** @ignore Called by the grid automatically. Do not call directly. */
35263     init : function(grid){
35264         this.grid = grid;
35265         this.initEvents();
35266     },
35267
35268     /**
35269      * Locks the selections.
35270      */
35271     lock : function(){
35272         this.locked = true;
35273     },
35274
35275     /**
35276      * Unlocks the selections.
35277      */
35278     unlock : function(){
35279         this.locked = false;
35280     },
35281
35282     /**
35283      * Returns true if the selections are locked.
35284      * @return {Boolean}
35285      */
35286     isLocked : function(){
35287         return this.locked;
35288     }
35289 });/*
35290  * Based on:
35291  * Ext JS Library 1.1.1
35292  * Copyright(c) 2006-2007, Ext JS, LLC.
35293  *
35294  * Originally Released Under LGPL - original licence link has changed is not relivant.
35295  *
35296  * Fork - LGPL
35297  * <script type="text/javascript">
35298  */
35299 /**
35300  * @extends Roo.grid.AbstractSelectionModel
35301  * @class Roo.grid.RowSelectionModel
35302  * The default SelectionModel used by {@link Roo.grid.Grid}.
35303  * It supports multiple selections and keyboard selection/navigation. 
35304  * @constructor
35305  * @param {Object} config
35306  */
35307 Roo.grid.RowSelectionModel = function(config){
35308     Roo.apply(this, config);
35309     this.selections = new Roo.util.MixedCollection(false, function(o){
35310         return o.id;
35311     });
35312
35313     this.last = false;
35314     this.lastActive = false;
35315
35316     this.addEvents({
35317         /**
35318              * @event selectionchange
35319              * Fires when the selection changes
35320              * @param {SelectionModel} this
35321              */
35322             "selectionchange" : true,
35323         /**
35324              * @event afterselectionchange
35325              * Fires after the selection changes (eg. by key press or clicking)
35326              * @param {SelectionModel} this
35327              */
35328             "afterselectionchange" : true,
35329         /**
35330              * @event beforerowselect
35331              * Fires when a row is selected being selected, return false to cancel.
35332              * @param {SelectionModel} this
35333              * @param {Number} rowIndex The selected index
35334              * @param {Boolean} keepExisting False if other selections will be cleared
35335              */
35336             "beforerowselect" : true,
35337         /**
35338              * @event rowselect
35339              * Fires when a row is selected.
35340              * @param {SelectionModel} this
35341              * @param {Number} rowIndex The selected index
35342              * @param {Roo.data.Record} r The record
35343              */
35344             "rowselect" : true,
35345         /**
35346              * @event rowdeselect
35347              * Fires when a row is deselected.
35348              * @param {SelectionModel} this
35349              * @param {Number} rowIndex The selected index
35350              */
35351         "rowdeselect" : true
35352     });
35353     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
35354     this.locked = false;
35355 };
35356
35357 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
35358     /**
35359      * @cfg {Boolean} singleSelect
35360      * True to allow selection of only one row at a time (defaults to false)
35361      */
35362     singleSelect : false,
35363
35364     // private
35365     initEvents : function(){
35366
35367         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
35368             this.grid.on("mousedown", this.handleMouseDown, this);
35369         }else{ // allow click to work like normal
35370             this.grid.on("rowclick", this.handleDragableRowClick, this);
35371         }
35372
35373         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
35374             "up" : function(e){
35375                 if(!e.shiftKey){
35376                     this.selectPrevious(e.shiftKey);
35377                 }else if(this.last !== false && this.lastActive !== false){
35378                     var last = this.last;
35379                     this.selectRange(this.last,  this.lastActive-1);
35380                     this.grid.getView().focusRow(this.lastActive);
35381                     if(last !== false){
35382                         this.last = last;
35383                     }
35384                 }else{
35385                     this.selectFirstRow();
35386                 }
35387                 this.fireEvent("afterselectionchange", this);
35388             },
35389             "down" : function(e){
35390                 if(!e.shiftKey){
35391                     this.selectNext(e.shiftKey);
35392                 }else if(this.last !== false && this.lastActive !== false){
35393                     var last = this.last;
35394                     this.selectRange(this.last,  this.lastActive+1);
35395                     this.grid.getView().focusRow(this.lastActive);
35396                     if(last !== false){
35397                         this.last = last;
35398                     }
35399                 }else{
35400                     this.selectFirstRow();
35401                 }
35402                 this.fireEvent("afterselectionchange", this);
35403             },
35404             scope: this
35405         });
35406
35407         var view = this.grid.view;
35408         view.on("refresh", this.onRefresh, this);
35409         view.on("rowupdated", this.onRowUpdated, this);
35410         view.on("rowremoved", this.onRemove, this);
35411     },
35412
35413     // private
35414     onRefresh : function(){
35415         var ds = this.grid.dataSource, i, v = this.grid.view;
35416         var s = this.selections;
35417         s.each(function(r){
35418             if((i = ds.indexOfId(r.id)) != -1){
35419                 v.onRowSelect(i);
35420                 s.add(ds.getAt(i)); // updating the selection relate data
35421             }else{
35422                 s.remove(r);
35423             }
35424         });
35425     },
35426
35427     // private
35428     onRemove : function(v, index, r){
35429         this.selections.remove(r);
35430     },
35431
35432     // private
35433     onRowUpdated : function(v, index, r){
35434         if(this.isSelected(r)){
35435             v.onRowSelect(index);
35436         }
35437     },
35438
35439     /**
35440      * Select records.
35441      * @param {Array} records The records to select
35442      * @param {Boolean} keepExisting (optional) True to keep existing selections
35443      */
35444     selectRecords : function(records, keepExisting){
35445         if(!keepExisting){
35446             this.clearSelections();
35447         }
35448         var ds = this.grid.dataSource;
35449         for(var i = 0, len = records.length; i < len; i++){
35450             this.selectRow(ds.indexOf(records[i]), true);
35451         }
35452     },
35453
35454     /**
35455      * Gets the number of selected rows.
35456      * @return {Number}
35457      */
35458     getCount : function(){
35459         return this.selections.length;
35460     },
35461
35462     /**
35463      * Selects the first row in the grid.
35464      */
35465     selectFirstRow : function(){
35466         this.selectRow(0);
35467     },
35468
35469     /**
35470      * Select the last row.
35471      * @param {Boolean} keepExisting (optional) True to keep existing selections
35472      */
35473     selectLastRow : function(keepExisting){
35474         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
35475     },
35476
35477     /**
35478      * Selects the row immediately following the last selected row.
35479      * @param {Boolean} keepExisting (optional) True to keep existing selections
35480      */
35481     selectNext : function(keepExisting){
35482         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
35483             this.selectRow(this.last+1, keepExisting);
35484             this.grid.getView().focusRow(this.last);
35485         }
35486     },
35487
35488     /**
35489      * Selects the row that precedes the last selected row.
35490      * @param {Boolean} keepExisting (optional) True to keep existing selections
35491      */
35492     selectPrevious : function(keepExisting){
35493         if(this.last){
35494             this.selectRow(this.last-1, keepExisting);
35495             this.grid.getView().focusRow(this.last);
35496         }
35497     },
35498
35499     /**
35500      * Returns the selected records
35501      * @return {Array} Array of selected records
35502      */
35503     getSelections : function(){
35504         return [].concat(this.selections.items);
35505     },
35506
35507     /**
35508      * Returns the first selected record.
35509      * @return {Record}
35510      */
35511     getSelected : function(){
35512         return this.selections.itemAt(0);
35513     },
35514
35515
35516     /**
35517      * Clears all selections.
35518      */
35519     clearSelections : function(fast){
35520         if(this.locked) {
35521             return;
35522         }
35523         if(fast !== true){
35524             var ds = this.grid.dataSource;
35525             var s = this.selections;
35526             s.each(function(r){
35527                 this.deselectRow(ds.indexOfId(r.id));
35528             }, this);
35529             s.clear();
35530         }else{
35531             this.selections.clear();
35532         }
35533         this.last = false;
35534     },
35535
35536
35537     /**
35538      * Selects all rows.
35539      */
35540     selectAll : function(){
35541         if(this.locked) {
35542             return;
35543         }
35544         this.selections.clear();
35545         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
35546             this.selectRow(i, true);
35547         }
35548     },
35549
35550     /**
35551      * Returns True if there is a selection.
35552      * @return {Boolean}
35553      */
35554     hasSelection : function(){
35555         return this.selections.length > 0;
35556     },
35557
35558     /**
35559      * Returns True if the specified row is selected.
35560      * @param {Number/Record} record The record or index of the record to check
35561      * @return {Boolean}
35562      */
35563     isSelected : function(index){
35564         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
35565         return (r && this.selections.key(r.id) ? true : false);
35566     },
35567
35568     /**
35569      * Returns True if the specified record id is selected.
35570      * @param {String} id The id of record to check
35571      * @return {Boolean}
35572      */
35573     isIdSelected : function(id){
35574         return (this.selections.key(id) ? true : false);
35575     },
35576
35577     // private
35578     handleMouseDown : function(e, t){
35579         var view = this.grid.getView(), rowIndex;
35580         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
35581             return;
35582         };
35583         if(e.shiftKey && this.last !== false){
35584             var last = this.last;
35585             this.selectRange(last, rowIndex, e.ctrlKey);
35586             this.last = last; // reset the last
35587             view.focusRow(rowIndex);
35588         }else{
35589             var isSelected = this.isSelected(rowIndex);
35590             if(e.button !== 0 && isSelected){
35591                 view.focusRow(rowIndex);
35592             }else if(e.ctrlKey && isSelected){
35593                 this.deselectRow(rowIndex);
35594             }else if(!isSelected){
35595                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
35596                 view.focusRow(rowIndex);
35597             }
35598         }
35599         this.fireEvent("afterselectionchange", this);
35600     },
35601     // private
35602     handleDragableRowClick :  function(grid, rowIndex, e) 
35603     {
35604         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
35605             this.selectRow(rowIndex, false);
35606             grid.view.focusRow(rowIndex);
35607              this.fireEvent("afterselectionchange", this);
35608         }
35609     },
35610     
35611     /**
35612      * Selects multiple rows.
35613      * @param {Array} rows Array of the indexes of the row to select
35614      * @param {Boolean} keepExisting (optional) True to keep existing selections
35615      */
35616     selectRows : function(rows, keepExisting){
35617         if(!keepExisting){
35618             this.clearSelections();
35619         }
35620         for(var i = 0, len = rows.length; i < len; i++){
35621             this.selectRow(rows[i], true);
35622         }
35623     },
35624
35625     /**
35626      * Selects a range of rows. All rows in between startRow and endRow are also selected.
35627      * @param {Number} startRow The index of the first row in the range
35628      * @param {Number} endRow The index of the last row in the range
35629      * @param {Boolean} keepExisting (optional) True to retain existing selections
35630      */
35631     selectRange : function(startRow, endRow, keepExisting){
35632         if(this.locked) {
35633             return;
35634         }
35635         if(!keepExisting){
35636             this.clearSelections();
35637         }
35638         if(startRow <= endRow){
35639             for(var i = startRow; i <= endRow; i++){
35640                 this.selectRow(i, true);
35641             }
35642         }else{
35643             for(var i = startRow; i >= endRow; i--){
35644                 this.selectRow(i, true);
35645             }
35646         }
35647     },
35648
35649     /**
35650      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
35651      * @param {Number} startRow The index of the first row in the range
35652      * @param {Number} endRow The index of the last row in the range
35653      */
35654     deselectRange : function(startRow, endRow, preventViewNotify){
35655         if(this.locked) {
35656             return;
35657         }
35658         for(var i = startRow; i <= endRow; i++){
35659             this.deselectRow(i, preventViewNotify);
35660         }
35661     },
35662
35663     /**
35664      * Selects a row.
35665      * @param {Number} row The index of the row to select
35666      * @param {Boolean} keepExisting (optional) True to keep existing selections
35667      */
35668     selectRow : function(index, keepExisting, preventViewNotify){
35669         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
35670             return;
35671         }
35672         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
35673             if(!keepExisting || this.singleSelect){
35674                 this.clearSelections();
35675             }
35676             var r = this.grid.dataSource.getAt(index);
35677             this.selections.add(r);
35678             this.last = this.lastActive = index;
35679             if(!preventViewNotify){
35680                 this.grid.getView().onRowSelect(index);
35681             }
35682             this.fireEvent("rowselect", this, index, r);
35683             this.fireEvent("selectionchange", this);
35684         }
35685     },
35686
35687     /**
35688      * Deselects a row.
35689      * @param {Number} row The index of the row to deselect
35690      */
35691     deselectRow : function(index, preventViewNotify){
35692         if(this.locked) {
35693             return;
35694         }
35695         if(this.last == index){
35696             this.last = false;
35697         }
35698         if(this.lastActive == index){
35699             this.lastActive = false;
35700         }
35701         var r = this.grid.dataSource.getAt(index);
35702         this.selections.remove(r);
35703         if(!preventViewNotify){
35704             this.grid.getView().onRowDeselect(index);
35705         }
35706         this.fireEvent("rowdeselect", this, index);
35707         this.fireEvent("selectionchange", this);
35708     },
35709
35710     // private
35711     restoreLast : function(){
35712         if(this._last){
35713             this.last = this._last;
35714         }
35715     },
35716
35717     // private
35718     acceptsNav : function(row, col, cm){
35719         return !cm.isHidden(col) && cm.isCellEditable(col, row);
35720     },
35721
35722     // private
35723     onEditorKey : function(field, e){
35724         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
35725         if(k == e.TAB){
35726             e.stopEvent();
35727             ed.completeEdit();
35728             if(e.shiftKey){
35729                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
35730             }else{
35731                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
35732             }
35733         }else if(k == e.ENTER && !e.ctrlKey){
35734             e.stopEvent();
35735             ed.completeEdit();
35736             if(e.shiftKey){
35737                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
35738             }else{
35739                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
35740             }
35741         }else if(k == e.ESC){
35742             ed.cancelEdit();
35743         }
35744         if(newCell){
35745             g.startEditing(newCell[0], newCell[1]);
35746         }
35747     }
35748 });/*
35749  * Based on:
35750  * Ext JS Library 1.1.1
35751  * Copyright(c) 2006-2007, Ext JS, LLC.
35752  *
35753  * Originally Released Under LGPL - original licence link has changed is not relivant.
35754  *
35755  * Fork - LGPL
35756  * <script type="text/javascript">
35757  */
35758 /**
35759  * @class Roo.grid.CellSelectionModel
35760  * @extends Roo.grid.AbstractSelectionModel
35761  * This class provides the basic implementation for cell selection in a grid.
35762  * @constructor
35763  * @param {Object} config The object containing the configuration of this model.
35764  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
35765  */
35766 Roo.grid.CellSelectionModel = function(config){
35767     Roo.apply(this, config);
35768
35769     this.selection = null;
35770
35771     this.addEvents({
35772         /**
35773              * @event beforerowselect
35774              * Fires before a cell is selected.
35775              * @param {SelectionModel} this
35776              * @param {Number} rowIndex The selected row index
35777              * @param {Number} colIndex The selected cell index
35778              */
35779             "beforecellselect" : true,
35780         /**
35781              * @event cellselect
35782              * Fires when a cell is selected.
35783              * @param {SelectionModel} this
35784              * @param {Number} rowIndex The selected row index
35785              * @param {Number} colIndex The selected cell index
35786              */
35787             "cellselect" : true,
35788         /**
35789              * @event selectionchange
35790              * Fires when the active selection changes.
35791              * @param {SelectionModel} this
35792              * @param {Object} selection null for no selection or an object (o) with two properties
35793                 <ul>
35794                 <li>o.record: the record object for the row the selection is in</li>
35795                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
35796                 </ul>
35797              */
35798             "selectionchange" : true,
35799         /**
35800              * @event tabend
35801              * Fires when the tab (or enter) was pressed on the last editable cell
35802              * You can use this to trigger add new row.
35803              * @param {SelectionModel} this
35804              */
35805             "tabend" : true,
35806          /**
35807              * @event beforeeditnext
35808              * Fires before the next editable sell is made active
35809              * You can use this to skip to another cell or fire the tabend
35810              *    if you set cell to false
35811              * @param {Object} eventdata object : { cell : [ row, col ] } 
35812              */
35813             "beforeeditnext" : true
35814     });
35815     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
35816 };
35817
35818 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
35819     
35820     enter_is_tab: false,
35821
35822     /** @ignore */
35823     initEvents : function(){
35824         this.grid.on("mousedown", this.handleMouseDown, this);
35825         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
35826         var view = this.grid.view;
35827         view.on("refresh", this.onViewChange, this);
35828         view.on("rowupdated", this.onRowUpdated, this);
35829         view.on("beforerowremoved", this.clearSelections, this);
35830         view.on("beforerowsinserted", this.clearSelections, this);
35831         if(this.grid.isEditor){
35832             this.grid.on("beforeedit", this.beforeEdit,  this);
35833         }
35834     },
35835
35836         //private
35837     beforeEdit : function(e){
35838         this.select(e.row, e.column, false, true, e.record);
35839     },
35840
35841         //private
35842     onRowUpdated : function(v, index, r){
35843         if(this.selection && this.selection.record == r){
35844             v.onCellSelect(index, this.selection.cell[1]);
35845         }
35846     },
35847
35848         //private
35849     onViewChange : function(){
35850         this.clearSelections(true);
35851     },
35852
35853         /**
35854          * Returns the currently selected cell,.
35855          * @return {Array} The selected cell (row, column) or null if none selected.
35856          */
35857     getSelectedCell : function(){
35858         return this.selection ? this.selection.cell : null;
35859     },
35860
35861     /**
35862      * Clears all selections.
35863      * @param {Boolean} true to prevent the gridview from being notified about the change.
35864      */
35865     clearSelections : function(preventNotify){
35866         var s = this.selection;
35867         if(s){
35868             if(preventNotify !== true){
35869                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
35870             }
35871             this.selection = null;
35872             this.fireEvent("selectionchange", this, null);
35873         }
35874     },
35875
35876     /**
35877      * Returns true if there is a selection.
35878      * @return {Boolean}
35879      */
35880     hasSelection : function(){
35881         return this.selection ? true : false;
35882     },
35883
35884     /** @ignore */
35885     handleMouseDown : function(e, t){
35886         var v = this.grid.getView();
35887         if(this.isLocked()){
35888             return;
35889         };
35890         var row = v.findRowIndex(t);
35891         var cell = v.findCellIndex(t);
35892         if(row !== false && cell !== false){
35893             this.select(row, cell);
35894         }
35895     },
35896
35897     /**
35898      * Selects a cell.
35899      * @param {Number} rowIndex
35900      * @param {Number} collIndex
35901      */
35902     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
35903         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
35904             this.clearSelections();
35905             r = r || this.grid.dataSource.getAt(rowIndex);
35906             this.selection = {
35907                 record : r,
35908                 cell : [rowIndex, colIndex]
35909             };
35910             if(!preventViewNotify){
35911                 var v = this.grid.getView();
35912                 v.onCellSelect(rowIndex, colIndex);
35913                 if(preventFocus !== true){
35914                     v.focusCell(rowIndex, colIndex);
35915                 }
35916             }
35917             this.fireEvent("cellselect", this, rowIndex, colIndex);
35918             this.fireEvent("selectionchange", this, this.selection);
35919         }
35920     },
35921
35922         //private
35923     isSelectable : function(rowIndex, colIndex, cm){
35924         return !cm.isHidden(colIndex);
35925     },
35926
35927     /** @ignore */
35928     handleKeyDown : function(e){
35929         //Roo.log('Cell Sel Model handleKeyDown');
35930         if(!e.isNavKeyPress()){
35931             return;
35932         }
35933         var g = this.grid, s = this.selection;
35934         if(!s){
35935             e.stopEvent();
35936             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
35937             if(cell){
35938                 this.select(cell[0], cell[1]);
35939             }
35940             return;
35941         }
35942         var sm = this;
35943         var walk = function(row, col, step){
35944             return g.walkCells(row, col, step, sm.isSelectable,  sm);
35945         };
35946         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
35947         var newCell;
35948
35949       
35950
35951         switch(k){
35952             case e.TAB:
35953                 // handled by onEditorKey
35954                 if (g.isEditor && g.editing) {
35955                     return;
35956                 }
35957                 if(e.shiftKey) {
35958                     newCell = walk(r, c-1, -1);
35959                 } else {
35960                     newCell = walk(r, c+1, 1);
35961                 }
35962                 break;
35963             
35964             case e.DOWN:
35965                newCell = walk(r+1, c, 1);
35966                 break;
35967             
35968             case e.UP:
35969                 newCell = walk(r-1, c, -1);
35970                 break;
35971             
35972             case e.RIGHT:
35973                 newCell = walk(r, c+1, 1);
35974                 break;
35975             
35976             case e.LEFT:
35977                 newCell = walk(r, c-1, -1);
35978                 break;
35979             
35980             case e.ENTER:
35981                 
35982                 if(g.isEditor && !g.editing){
35983                    g.startEditing(r, c);
35984                    e.stopEvent();
35985                    return;
35986                 }
35987                 
35988                 
35989              break;
35990         };
35991         if(newCell){
35992             this.select(newCell[0], newCell[1]);
35993             e.stopEvent();
35994             
35995         }
35996     },
35997
35998     acceptsNav : function(row, col, cm){
35999         return !cm.isHidden(col) && cm.isCellEditable(col, row);
36000     },
36001     /**
36002      * Selects a cell.
36003      * @param {Number} field (not used) - as it's normally used as a listener
36004      * @param {Number} e - event - fake it by using
36005      *
36006      * var e = Roo.EventObjectImpl.prototype;
36007      * e.keyCode = e.TAB
36008      *
36009      * 
36010      */
36011     onEditorKey : function(field, e){
36012         
36013         var k = e.getKey(),
36014             newCell,
36015             g = this.grid,
36016             ed = g.activeEditor,
36017             forward = false;
36018         ///Roo.log('onEditorKey' + k);
36019         
36020         
36021         if (this.enter_is_tab && k == e.ENTER) {
36022             k = e.TAB;
36023         }
36024         
36025         if(k == e.TAB){
36026             if(e.shiftKey){
36027                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36028             }else{
36029                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36030                 forward = true;
36031             }
36032             
36033             e.stopEvent();
36034             
36035         } else if(k == e.ENTER &&  !e.ctrlKey){
36036             ed.completeEdit();
36037             e.stopEvent();
36038             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36039         
36040                 } else if(k == e.ESC){
36041             ed.cancelEdit();
36042         }
36043                 
36044         if (newCell) {
36045             var ecall = { cell : newCell, forward : forward };
36046             this.fireEvent('beforeeditnext', ecall );
36047             newCell = ecall.cell;
36048                         forward = ecall.forward;
36049         }
36050                 
36051         if(newCell){
36052             //Roo.log('next cell after edit');
36053             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
36054         } else if (forward) {
36055             // tabbed past last
36056             this.fireEvent.defer(100, this, ['tabend',this]);
36057         }
36058     }
36059 });/*
36060  * Based on:
36061  * Ext JS Library 1.1.1
36062  * Copyright(c) 2006-2007, Ext JS, LLC.
36063  *
36064  * Originally Released Under LGPL - original licence link has changed is not relivant.
36065  *
36066  * Fork - LGPL
36067  * <script type="text/javascript">
36068  */
36069  
36070 /**
36071  * @class Roo.grid.EditorGrid
36072  * @extends Roo.grid.Grid
36073  * Class for creating and editable grid.
36074  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
36075  * The container MUST have some type of size defined for the grid to fill. The container will be 
36076  * automatically set to position relative if it isn't already.
36077  * @param {Object} dataSource The data model to bind to
36078  * @param {Object} colModel The column model with info about this grid's columns
36079  */
36080 Roo.grid.EditorGrid = function(container, config){
36081     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
36082     this.getGridEl().addClass("xedit-grid");
36083
36084     if(!this.selModel){
36085         this.selModel = new Roo.grid.CellSelectionModel();
36086     }
36087
36088     this.activeEditor = null;
36089
36090         this.addEvents({
36091             /**
36092              * @event beforeedit
36093              * Fires before cell editing is triggered. The edit event object has the following properties <br />
36094              * <ul style="padding:5px;padding-left:16px;">
36095              * <li>grid - This grid</li>
36096              * <li>record - The record being edited</li>
36097              * <li>field - The field name being edited</li>
36098              * <li>value - The value for the field being edited.</li>
36099              * <li>row - The grid row index</li>
36100              * <li>column - The grid column index</li>
36101              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36102              * </ul>
36103              * @param {Object} e An edit event (see above for description)
36104              */
36105             "beforeedit" : true,
36106             /**
36107              * @event afteredit
36108              * Fires after a cell is edited. <br />
36109              * <ul style="padding:5px;padding-left:16px;">
36110              * <li>grid - This grid</li>
36111              * <li>record - The record being edited</li>
36112              * <li>field - The field name being edited</li>
36113              * <li>value - The value being set</li>
36114              * <li>originalValue - The original value for the field, before the edit.</li>
36115              * <li>row - The grid row index</li>
36116              * <li>column - The grid column index</li>
36117              * </ul>
36118              * @param {Object} e An edit event (see above for description)
36119              */
36120             "afteredit" : true,
36121             /**
36122              * @event validateedit
36123              * Fires after a cell is edited, but before the value is set in the record. 
36124          * You can use this to modify the value being set in the field, Return false
36125              * to cancel the change. The edit event object has the following properties <br />
36126              * <ul style="padding:5px;padding-left:16px;">
36127          * <li>editor - This editor</li>
36128              * <li>grid - This grid</li>
36129              * <li>record - The record being edited</li>
36130              * <li>field - The field name being edited</li>
36131              * <li>value - The value being set</li>
36132              * <li>originalValue - The original value for the field, before the edit.</li>
36133              * <li>row - The grid row index</li>
36134              * <li>column - The grid column index</li>
36135              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36136              * </ul>
36137              * @param {Object} e An edit event (see above for description)
36138              */
36139             "validateedit" : true
36140         });
36141     this.on("bodyscroll", this.stopEditing,  this);
36142     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
36143 };
36144
36145 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
36146     /**
36147      * @cfg {Number} clicksToEdit
36148      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
36149      */
36150     clicksToEdit: 2,
36151
36152     // private
36153     isEditor : true,
36154     // private
36155     trackMouseOver: false, // causes very odd FF errors
36156
36157     onCellDblClick : function(g, row, col){
36158         this.startEditing(row, col);
36159     },
36160
36161     onEditComplete : function(ed, value, startValue){
36162         this.editing = false;
36163         this.activeEditor = null;
36164         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
36165         var r = ed.record;
36166         var field = this.colModel.getDataIndex(ed.col);
36167         var e = {
36168             grid: this,
36169             record: r,
36170             field: field,
36171             originalValue: startValue,
36172             value: value,
36173             row: ed.row,
36174             column: ed.col,
36175             cancel:false,
36176             editor: ed
36177         };
36178         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
36179         cell.show();
36180           
36181         if(String(value) !== String(startValue)){
36182             
36183             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
36184                 r.set(field, e.value);
36185                 // if we are dealing with a combo box..
36186                 // then we also set the 'name' colum to be the displayField
36187                 if (ed.field.displayField && ed.field.name) {
36188                     r.set(ed.field.name, ed.field.el.dom.value);
36189                 }
36190                 
36191                 delete e.cancel; //?? why!!!
36192                 this.fireEvent("afteredit", e);
36193             }
36194         } else {
36195             this.fireEvent("afteredit", e); // always fire it!
36196         }
36197         this.view.focusCell(ed.row, ed.col);
36198     },
36199
36200     /**
36201      * Starts editing the specified for the specified row/column
36202      * @param {Number} rowIndex
36203      * @param {Number} colIndex
36204      */
36205     startEditing : function(row, col){
36206         this.stopEditing();
36207         if(this.colModel.isCellEditable(col, row)){
36208             this.view.ensureVisible(row, col, true);
36209           
36210             var r = this.dataSource.getAt(row);
36211             var field = this.colModel.getDataIndex(col);
36212             var cell = Roo.get(this.view.getCell(row,col));
36213             var e = {
36214                 grid: this,
36215                 record: r,
36216                 field: field,
36217                 value: r.data[field],
36218                 row: row,
36219                 column: col,
36220                 cancel:false 
36221             };
36222             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
36223                 this.editing = true;
36224                 var ed = this.colModel.getCellEditor(col, row);
36225                 
36226                 if (!ed) {
36227                     return;
36228                 }
36229                 if(!ed.rendered){
36230                     ed.render(ed.parentEl || document.body);
36231                 }
36232                 ed.field.reset();
36233                
36234                 cell.hide();
36235                 
36236                 (function(){ // complex but required for focus issues in safari, ie and opera
36237                     ed.row = row;
36238                     ed.col = col;
36239                     ed.record = r;
36240                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
36241                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
36242                     this.activeEditor = ed;
36243                     var v = r.data[field];
36244                     ed.startEdit(this.view.getCell(row, col), v);
36245                     // combo's with 'displayField and name set
36246                     if (ed.field.displayField && ed.field.name) {
36247                         ed.field.el.dom.value = r.data[ed.field.name];
36248                     }
36249                     
36250                     
36251                 }).defer(50, this);
36252             }
36253         }
36254     },
36255         
36256     /**
36257      * Stops any active editing
36258      */
36259     stopEditing : function(){
36260         if(this.activeEditor){
36261             this.activeEditor.completeEdit();
36262         }
36263         this.activeEditor = null;
36264     },
36265         
36266          /**
36267      * Called to get grid's drag proxy text, by default returns this.ddText.
36268      * @return {String}
36269      */
36270     getDragDropText : function(){
36271         var count = this.selModel.getSelectedCell() ? 1 : 0;
36272         return String.format(this.ddText, count, count == 1 ? '' : 's');
36273     }
36274         
36275 });/*
36276  * Based on:
36277  * Ext JS Library 1.1.1
36278  * Copyright(c) 2006-2007, Ext JS, LLC.
36279  *
36280  * Originally Released Under LGPL - original licence link has changed is not relivant.
36281  *
36282  * Fork - LGPL
36283  * <script type="text/javascript">
36284  */
36285
36286 // private - not really -- you end up using it !
36287 // This is a support class used internally by the Grid components
36288
36289 /**
36290  * @class Roo.grid.GridEditor
36291  * @extends Roo.Editor
36292  * Class for creating and editable grid elements.
36293  * @param {Object} config any settings (must include field)
36294  */
36295 Roo.grid.GridEditor = function(field, config){
36296     if (!config && field.field) {
36297         config = field;
36298         field = Roo.factory(config.field, Roo.form);
36299     }
36300     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
36301     field.monitorTab = false;
36302 };
36303
36304 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
36305     
36306     /**
36307      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
36308      */
36309     
36310     alignment: "tl-tl",
36311     autoSize: "width",
36312     hideEl : false,
36313     cls: "x-small-editor x-grid-editor",
36314     shim:false,
36315     shadow:"frame"
36316 });/*
36317  * Based on:
36318  * Ext JS Library 1.1.1
36319  * Copyright(c) 2006-2007, Ext JS, LLC.
36320  *
36321  * Originally Released Under LGPL - original licence link has changed is not relivant.
36322  *
36323  * Fork - LGPL
36324  * <script type="text/javascript">
36325  */
36326   
36327
36328   
36329 Roo.grid.PropertyRecord = Roo.data.Record.create([
36330     {name:'name',type:'string'},  'value'
36331 ]);
36332
36333
36334 Roo.grid.PropertyStore = function(grid, source){
36335     this.grid = grid;
36336     this.store = new Roo.data.Store({
36337         recordType : Roo.grid.PropertyRecord
36338     });
36339     this.store.on('update', this.onUpdate,  this);
36340     if(source){
36341         this.setSource(source);
36342     }
36343     Roo.grid.PropertyStore.superclass.constructor.call(this);
36344 };
36345
36346
36347
36348 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
36349     setSource : function(o){
36350         this.source = o;
36351         this.store.removeAll();
36352         var data = [];
36353         for(var k in o){
36354             if(this.isEditableValue(o[k])){
36355                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
36356             }
36357         }
36358         this.store.loadRecords({records: data}, {}, true);
36359     },
36360
36361     onUpdate : function(ds, record, type){
36362         if(type == Roo.data.Record.EDIT){
36363             var v = record.data['value'];
36364             var oldValue = record.modified['value'];
36365             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
36366                 this.source[record.id] = v;
36367                 record.commit();
36368                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
36369             }else{
36370                 record.reject();
36371             }
36372         }
36373     },
36374
36375     getProperty : function(row){
36376        return this.store.getAt(row);
36377     },
36378
36379     isEditableValue: function(val){
36380         if(val && val instanceof Date){
36381             return true;
36382         }else if(typeof val == 'object' || typeof val == 'function'){
36383             return false;
36384         }
36385         return true;
36386     },
36387
36388     setValue : function(prop, value){
36389         this.source[prop] = value;
36390         this.store.getById(prop).set('value', value);
36391     },
36392
36393     getSource : function(){
36394         return this.source;
36395     }
36396 });
36397
36398 Roo.grid.PropertyColumnModel = function(grid, store){
36399     this.grid = grid;
36400     var g = Roo.grid;
36401     g.PropertyColumnModel.superclass.constructor.call(this, [
36402         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
36403         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
36404     ]);
36405     this.store = store;
36406     this.bselect = Roo.DomHelper.append(document.body, {
36407         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
36408             {tag: 'option', value: 'true', html: 'true'},
36409             {tag: 'option', value: 'false', html: 'false'}
36410         ]
36411     });
36412     Roo.id(this.bselect);
36413     var f = Roo.form;
36414     this.editors = {
36415         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
36416         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
36417         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
36418         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
36419         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
36420     };
36421     this.renderCellDelegate = this.renderCell.createDelegate(this);
36422     this.renderPropDelegate = this.renderProp.createDelegate(this);
36423 };
36424
36425 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
36426     
36427     
36428     nameText : 'Name',
36429     valueText : 'Value',
36430     
36431     dateFormat : 'm/j/Y',
36432     
36433     
36434     renderDate : function(dateVal){
36435         return dateVal.dateFormat(this.dateFormat);
36436     },
36437
36438     renderBool : function(bVal){
36439         return bVal ? 'true' : 'false';
36440     },
36441
36442     isCellEditable : function(colIndex, rowIndex){
36443         return colIndex == 1;
36444     },
36445
36446     getRenderer : function(col){
36447         return col == 1 ?
36448             this.renderCellDelegate : this.renderPropDelegate;
36449     },
36450
36451     renderProp : function(v){
36452         return this.getPropertyName(v);
36453     },
36454
36455     renderCell : function(val){
36456         var rv = val;
36457         if(val instanceof Date){
36458             rv = this.renderDate(val);
36459         }else if(typeof val == 'boolean'){
36460             rv = this.renderBool(val);
36461         }
36462         return Roo.util.Format.htmlEncode(rv);
36463     },
36464
36465     getPropertyName : function(name){
36466         var pn = this.grid.propertyNames;
36467         return pn && pn[name] ? pn[name] : name;
36468     },
36469
36470     getCellEditor : function(colIndex, rowIndex){
36471         var p = this.store.getProperty(rowIndex);
36472         var n = p.data['name'], val = p.data['value'];
36473         
36474         if(typeof(this.grid.customEditors[n]) == 'string'){
36475             return this.editors[this.grid.customEditors[n]];
36476         }
36477         if(typeof(this.grid.customEditors[n]) != 'undefined'){
36478             return this.grid.customEditors[n];
36479         }
36480         if(val instanceof Date){
36481             return this.editors['date'];
36482         }else if(typeof val == 'number'){
36483             return this.editors['number'];
36484         }else if(typeof val == 'boolean'){
36485             return this.editors['boolean'];
36486         }else{
36487             return this.editors['string'];
36488         }
36489     }
36490 });
36491
36492 /**
36493  * @class Roo.grid.PropertyGrid
36494  * @extends Roo.grid.EditorGrid
36495  * This class represents the  interface of a component based property grid control.
36496  * <br><br>Usage:<pre><code>
36497  var grid = new Roo.grid.PropertyGrid("my-container-id", {
36498       
36499  });
36500  // set any options
36501  grid.render();
36502  * </code></pre>
36503   
36504  * @constructor
36505  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
36506  * The container MUST have some type of size defined for the grid to fill. The container will be
36507  * automatically set to position relative if it isn't already.
36508  * @param {Object} config A config object that sets properties on this grid.
36509  */
36510 Roo.grid.PropertyGrid = function(container, config){
36511     config = config || {};
36512     var store = new Roo.grid.PropertyStore(this);
36513     this.store = store;
36514     var cm = new Roo.grid.PropertyColumnModel(this, store);
36515     store.store.sort('name', 'ASC');
36516     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
36517         ds: store.store,
36518         cm: cm,
36519         enableColLock:false,
36520         enableColumnMove:false,
36521         stripeRows:false,
36522         trackMouseOver: false,
36523         clicksToEdit:1
36524     }, config));
36525     this.getGridEl().addClass('x-props-grid');
36526     this.lastEditRow = null;
36527     this.on('columnresize', this.onColumnResize, this);
36528     this.addEvents({
36529          /**
36530              * @event beforepropertychange
36531              * Fires before a property changes (return false to stop?)
36532              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
36533              * @param {String} id Record Id
36534              * @param {String} newval New Value
36535          * @param {String} oldval Old Value
36536              */
36537         "beforepropertychange": true,
36538         /**
36539              * @event propertychange
36540              * Fires after a property changes
36541              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
36542              * @param {String} id Record Id
36543              * @param {String} newval New Value
36544          * @param {String} oldval Old Value
36545              */
36546         "propertychange": true
36547     });
36548     this.customEditors = this.customEditors || {};
36549 };
36550 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
36551     
36552      /**
36553      * @cfg {Object} customEditors map of colnames=> custom editors.
36554      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
36555      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
36556      * false disables editing of the field.
36557          */
36558     
36559       /**
36560      * @cfg {Object} propertyNames map of property Names to their displayed value
36561          */
36562     
36563     render : function(){
36564         Roo.grid.PropertyGrid.superclass.render.call(this);
36565         this.autoSize.defer(100, this);
36566     },
36567
36568     autoSize : function(){
36569         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
36570         if(this.view){
36571             this.view.fitColumns();
36572         }
36573     },
36574
36575     onColumnResize : function(){
36576         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
36577         this.autoSize();
36578     },
36579     /**
36580      * Sets the data for the Grid
36581      * accepts a Key => Value object of all the elements avaiable.
36582      * @param {Object} data  to appear in grid.
36583      */
36584     setSource : function(source){
36585         this.store.setSource(source);
36586         //this.autoSize();
36587     },
36588     /**
36589      * Gets all the data from the grid.
36590      * @return {Object} data  data stored in grid
36591      */
36592     getSource : function(){
36593         return this.store.getSource();
36594     }
36595 });/*
36596   
36597  * Licence LGPL
36598  
36599  */
36600  
36601 /**
36602  * @class Roo.grid.Calendar
36603  * @extends Roo.util.Grid
36604  * This class extends the Grid to provide a calendar widget
36605  * <br><br>Usage:<pre><code>
36606  var grid = new Roo.grid.Calendar("my-container-id", {
36607      ds: myDataStore,
36608      cm: myColModel,
36609      selModel: mySelectionModel,
36610      autoSizeColumns: true,
36611      monitorWindowResize: false,
36612      trackMouseOver: true
36613      eventstore : real data store..
36614  });
36615  // set any options
36616  grid.render();
36617   
36618   * @constructor
36619  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
36620  * The container MUST have some type of size defined for the grid to fill. The container will be
36621  * automatically set to position relative if it isn't already.
36622  * @param {Object} config A config object that sets properties on this grid.
36623  */
36624 Roo.grid.Calendar = function(container, config){
36625         // initialize the container
36626         this.container = Roo.get(container);
36627         this.container.update("");
36628         this.container.setStyle("overflow", "hidden");
36629     this.container.addClass('x-grid-container');
36630
36631     this.id = this.container.id;
36632
36633     Roo.apply(this, config);
36634     // check and correct shorthanded configs
36635     
36636     var rows = [];
36637     var d =1;
36638     for (var r = 0;r < 6;r++) {
36639         
36640         rows[r]=[];
36641         for (var c =0;c < 7;c++) {
36642             rows[r][c]= '';
36643         }
36644     }
36645     if (this.eventStore) {
36646         this.eventStore= Roo.factory(this.eventStore, Roo.data);
36647         this.eventStore.on('load',this.onLoad, this);
36648         this.eventStore.on('beforeload',this.clearEvents, this);
36649          
36650     }
36651     
36652     this.dataSource = new Roo.data.Store({
36653             proxy: new Roo.data.MemoryProxy(rows),
36654             reader: new Roo.data.ArrayReader({}, [
36655                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
36656     });
36657
36658     this.dataSource.load();
36659     this.ds = this.dataSource;
36660     this.ds.xmodule = this.xmodule || false;
36661     
36662     
36663     var cellRender = function(v,x,r)
36664     {
36665         return String.format(
36666             '<div class="fc-day  fc-widget-content"><div>' +
36667                 '<div class="fc-event-container"></div>' +
36668                 '<div class="fc-day-number">{0}</div>'+
36669                 
36670                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
36671             '</div></div>', v);
36672     
36673     }
36674     
36675     
36676     this.colModel = new Roo.grid.ColumnModel( [
36677         {
36678             xtype: 'ColumnModel',
36679             xns: Roo.grid,
36680             dataIndex : 'weekday0',
36681             header : 'Sunday',
36682             renderer : cellRender
36683         },
36684         {
36685             xtype: 'ColumnModel',
36686             xns: Roo.grid,
36687             dataIndex : 'weekday1',
36688             header : 'Monday',
36689             renderer : cellRender
36690         },
36691         {
36692             xtype: 'ColumnModel',
36693             xns: Roo.grid,
36694             dataIndex : 'weekday2',
36695             header : 'Tuesday',
36696             renderer : cellRender
36697         },
36698         {
36699             xtype: 'ColumnModel',
36700             xns: Roo.grid,
36701             dataIndex : 'weekday3',
36702             header : 'Wednesday',
36703             renderer : cellRender
36704         },
36705         {
36706             xtype: 'ColumnModel',
36707             xns: Roo.grid,
36708             dataIndex : 'weekday4',
36709             header : 'Thursday',
36710             renderer : cellRender
36711         },
36712         {
36713             xtype: 'ColumnModel',
36714             xns: Roo.grid,
36715             dataIndex : 'weekday5',
36716             header : 'Friday',
36717             renderer : cellRender
36718         },
36719         {
36720             xtype: 'ColumnModel',
36721             xns: Roo.grid,
36722             dataIndex : 'weekday6',
36723             header : 'Saturday',
36724             renderer : cellRender
36725         }
36726     ]);
36727     this.cm = this.colModel;
36728     this.cm.xmodule = this.xmodule || false;
36729  
36730         
36731           
36732     //this.selModel = new Roo.grid.CellSelectionModel();
36733     //this.sm = this.selModel;
36734     //this.selModel.init(this);
36735     
36736     
36737     if(this.width){
36738         this.container.setWidth(this.width);
36739     }
36740
36741     if(this.height){
36742         this.container.setHeight(this.height);
36743     }
36744     /** @private */
36745         this.addEvents({
36746         // raw events
36747         /**
36748          * @event click
36749          * The raw click event for the entire grid.
36750          * @param {Roo.EventObject} e
36751          */
36752         "click" : true,
36753         /**
36754          * @event dblclick
36755          * The raw dblclick event for the entire grid.
36756          * @param {Roo.EventObject} e
36757          */
36758         "dblclick" : true,
36759         /**
36760          * @event contextmenu
36761          * The raw contextmenu event for the entire grid.
36762          * @param {Roo.EventObject} e
36763          */
36764         "contextmenu" : true,
36765         /**
36766          * @event mousedown
36767          * The raw mousedown event for the entire grid.
36768          * @param {Roo.EventObject} e
36769          */
36770         "mousedown" : true,
36771         /**
36772          * @event mouseup
36773          * The raw mouseup event for the entire grid.
36774          * @param {Roo.EventObject} e
36775          */
36776         "mouseup" : true,
36777         /**
36778          * @event mouseover
36779          * The raw mouseover event for the entire grid.
36780          * @param {Roo.EventObject} e
36781          */
36782         "mouseover" : true,
36783         /**
36784          * @event mouseout
36785          * The raw mouseout event for the entire grid.
36786          * @param {Roo.EventObject} e
36787          */
36788         "mouseout" : true,
36789         /**
36790          * @event keypress
36791          * The raw keypress event for the entire grid.
36792          * @param {Roo.EventObject} e
36793          */
36794         "keypress" : true,
36795         /**
36796          * @event keydown
36797          * The raw keydown event for the entire grid.
36798          * @param {Roo.EventObject} e
36799          */
36800         "keydown" : true,
36801
36802         // custom events
36803
36804         /**
36805          * @event cellclick
36806          * Fires when a cell is clicked
36807          * @param {Grid} this
36808          * @param {Number} rowIndex
36809          * @param {Number} columnIndex
36810          * @param {Roo.EventObject} e
36811          */
36812         "cellclick" : true,
36813         /**
36814          * @event celldblclick
36815          * Fires when a cell is double clicked
36816          * @param {Grid} this
36817          * @param {Number} rowIndex
36818          * @param {Number} columnIndex
36819          * @param {Roo.EventObject} e
36820          */
36821         "celldblclick" : true,
36822         /**
36823          * @event rowclick
36824          * Fires when a row is clicked
36825          * @param {Grid} this
36826          * @param {Number} rowIndex
36827          * @param {Roo.EventObject} e
36828          */
36829         "rowclick" : true,
36830         /**
36831          * @event rowdblclick
36832          * Fires when a row is double clicked
36833          * @param {Grid} this
36834          * @param {Number} rowIndex
36835          * @param {Roo.EventObject} e
36836          */
36837         "rowdblclick" : true,
36838         /**
36839          * @event headerclick
36840          * Fires when a header is clicked
36841          * @param {Grid} this
36842          * @param {Number} columnIndex
36843          * @param {Roo.EventObject} e
36844          */
36845         "headerclick" : true,
36846         /**
36847          * @event headerdblclick
36848          * Fires when a header cell is double clicked
36849          * @param {Grid} this
36850          * @param {Number} columnIndex
36851          * @param {Roo.EventObject} e
36852          */
36853         "headerdblclick" : true,
36854         /**
36855          * @event rowcontextmenu
36856          * Fires when a row is right clicked
36857          * @param {Grid} this
36858          * @param {Number} rowIndex
36859          * @param {Roo.EventObject} e
36860          */
36861         "rowcontextmenu" : true,
36862         /**
36863          * @event cellcontextmenu
36864          * Fires when a cell is right clicked
36865          * @param {Grid} this
36866          * @param {Number} rowIndex
36867          * @param {Number} cellIndex
36868          * @param {Roo.EventObject} e
36869          */
36870          "cellcontextmenu" : true,
36871         /**
36872          * @event headercontextmenu
36873          * Fires when a header is right clicked
36874          * @param {Grid} this
36875          * @param {Number} columnIndex
36876          * @param {Roo.EventObject} e
36877          */
36878         "headercontextmenu" : true,
36879         /**
36880          * @event bodyscroll
36881          * Fires when the body element is scrolled
36882          * @param {Number} scrollLeft
36883          * @param {Number} scrollTop
36884          */
36885         "bodyscroll" : true,
36886         /**
36887          * @event columnresize
36888          * Fires when the user resizes a column
36889          * @param {Number} columnIndex
36890          * @param {Number} newSize
36891          */
36892         "columnresize" : true,
36893         /**
36894          * @event columnmove
36895          * Fires when the user moves a column
36896          * @param {Number} oldIndex
36897          * @param {Number} newIndex
36898          */
36899         "columnmove" : true,
36900         /**
36901          * @event startdrag
36902          * Fires when row(s) start being dragged
36903          * @param {Grid} this
36904          * @param {Roo.GridDD} dd The drag drop object
36905          * @param {event} e The raw browser event
36906          */
36907         "startdrag" : true,
36908         /**
36909          * @event enddrag
36910          * Fires when a drag operation is complete
36911          * @param {Grid} this
36912          * @param {Roo.GridDD} dd The drag drop object
36913          * @param {event} e The raw browser event
36914          */
36915         "enddrag" : true,
36916         /**
36917          * @event dragdrop
36918          * Fires when dragged row(s) are dropped on a valid DD target
36919          * @param {Grid} this
36920          * @param {Roo.GridDD} dd The drag drop object
36921          * @param {String} targetId The target drag drop object
36922          * @param {event} e The raw browser event
36923          */
36924         "dragdrop" : true,
36925         /**
36926          * @event dragover
36927          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
36928          * @param {Grid} this
36929          * @param {Roo.GridDD} dd The drag drop object
36930          * @param {String} targetId The target drag drop object
36931          * @param {event} e The raw browser event
36932          */
36933         "dragover" : true,
36934         /**
36935          * @event dragenter
36936          *  Fires when the dragged row(s) first cross another DD target while being dragged
36937          * @param {Grid} this
36938          * @param {Roo.GridDD} dd The drag drop object
36939          * @param {String} targetId The target drag drop object
36940          * @param {event} e The raw browser event
36941          */
36942         "dragenter" : true,
36943         /**
36944          * @event dragout
36945          * Fires when the dragged row(s) leave another DD target while being dragged
36946          * @param {Grid} this
36947          * @param {Roo.GridDD} dd The drag drop object
36948          * @param {String} targetId The target drag drop object
36949          * @param {event} e The raw browser event
36950          */
36951         "dragout" : true,
36952         /**
36953          * @event rowclass
36954          * Fires when a row is rendered, so you can change add a style to it.
36955          * @param {GridView} gridview   The grid view
36956          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
36957          */
36958         'rowclass' : true,
36959
36960         /**
36961          * @event render
36962          * Fires when the grid is rendered
36963          * @param {Grid} grid
36964          */
36965         'render' : true,
36966             /**
36967              * @event select
36968              * Fires when a date is selected
36969              * @param {DatePicker} this
36970              * @param {Date} date The selected date
36971              */
36972         'select': true,
36973         /**
36974              * @event monthchange
36975              * Fires when the displayed month changes 
36976              * @param {DatePicker} this
36977              * @param {Date} date The selected month
36978              */
36979         'monthchange': true,
36980         /**
36981              * @event evententer
36982              * Fires when mouse over an event
36983              * @param {Calendar} this
36984              * @param {event} Event
36985              */
36986         'evententer': true,
36987         /**
36988              * @event eventleave
36989              * Fires when the mouse leaves an
36990              * @param {Calendar} this
36991              * @param {event}
36992              */
36993         'eventleave': true,
36994         /**
36995              * @event eventclick
36996              * Fires when the mouse click an
36997              * @param {Calendar} this
36998              * @param {event}
36999              */
37000         'eventclick': true,
37001         /**
37002              * @event eventrender
37003              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
37004              * @param {Calendar} this
37005              * @param {data} data to be modified
37006              */
37007         'eventrender': true
37008         
37009     });
37010
37011     Roo.grid.Grid.superclass.constructor.call(this);
37012     this.on('render', function() {
37013         this.view.el.addClass('x-grid-cal'); 
37014         
37015         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
37016
37017     },this);
37018     
37019     if (!Roo.grid.Calendar.style) {
37020         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
37021             
37022             
37023             '.x-grid-cal .x-grid-col' :  {
37024                 height: 'auto !important',
37025                 'vertical-align': 'top'
37026             },
37027             '.x-grid-cal  .fc-event-hori' : {
37028                 height: '14px'
37029             }
37030              
37031             
37032         }, Roo.id());
37033     }
37034
37035     
37036     
37037 };
37038 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
37039     /**
37040      * @cfg {Store} eventStore The store that loads events.
37041      */
37042     eventStore : 25,
37043
37044      
37045     activeDate : false,
37046     startDay : 0,
37047     autoWidth : true,
37048     monitorWindowResize : false,
37049
37050     
37051     resizeColumns : function() {
37052         var col = (this.view.el.getWidth() / 7) - 3;
37053         // loop through cols, and setWidth
37054         for(var i =0 ; i < 7 ; i++){
37055             this.cm.setColumnWidth(i, col);
37056         }
37057     },
37058      setDate :function(date) {
37059         
37060         Roo.log('setDate?');
37061         
37062         this.resizeColumns();
37063         var vd = this.activeDate;
37064         this.activeDate = date;
37065 //        if(vd && this.el){
37066 //            var t = date.getTime();
37067 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
37068 //                Roo.log('using add remove');
37069 //                
37070 //                this.fireEvent('monthchange', this, date);
37071 //                
37072 //                this.cells.removeClass("fc-state-highlight");
37073 //                this.cells.each(function(c){
37074 //                   if(c.dateValue == t){
37075 //                       c.addClass("fc-state-highlight");
37076 //                       setTimeout(function(){
37077 //                            try{c.dom.firstChild.focus();}catch(e){}
37078 //                       }, 50);
37079 //                       return false;
37080 //                   }
37081 //                   return true;
37082 //                });
37083 //                return;
37084 //            }
37085 //        }
37086         
37087         var days = date.getDaysInMonth();
37088         
37089         var firstOfMonth = date.getFirstDateOfMonth();
37090         var startingPos = firstOfMonth.getDay()-this.startDay;
37091         
37092         if(startingPos < this.startDay){
37093             startingPos += 7;
37094         }
37095         
37096         var pm = date.add(Date.MONTH, -1);
37097         var prevStart = pm.getDaysInMonth()-startingPos;
37098 //        
37099         
37100         
37101         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37102         
37103         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
37104         //this.cells.addClassOnOver('fc-state-hover');
37105         
37106         var cells = this.cells.elements;
37107         var textEls = this.textNodes;
37108         
37109         //Roo.each(cells, function(cell){
37110         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
37111         //});
37112         
37113         days += startingPos;
37114
37115         // convert everything to numbers so it's fast
37116         var day = 86400000;
37117         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
37118         //Roo.log(d);
37119         //Roo.log(pm);
37120         //Roo.log(prevStart);
37121         
37122         var today = new Date().clearTime().getTime();
37123         var sel = date.clearTime().getTime();
37124         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
37125         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
37126         var ddMatch = this.disabledDatesRE;
37127         var ddText = this.disabledDatesText;
37128         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
37129         var ddaysText = this.disabledDaysText;
37130         var format = this.format;
37131         
37132         var setCellClass = function(cal, cell){
37133             
37134             //Roo.log('set Cell Class');
37135             cell.title = "";
37136             var t = d.getTime();
37137             
37138             //Roo.log(d);
37139             
37140             
37141             cell.dateValue = t;
37142             if(t == today){
37143                 cell.className += " fc-today";
37144                 cell.className += " fc-state-highlight";
37145                 cell.title = cal.todayText;
37146             }
37147             if(t == sel){
37148                 // disable highlight in other month..
37149                 cell.className += " fc-state-highlight";
37150                 
37151             }
37152             // disabling
37153             if(t < min) {
37154                 //cell.className = " fc-state-disabled";
37155                 cell.title = cal.minText;
37156                 return;
37157             }
37158             if(t > max) {
37159                 //cell.className = " fc-state-disabled";
37160                 cell.title = cal.maxText;
37161                 return;
37162             }
37163             if(ddays){
37164                 if(ddays.indexOf(d.getDay()) != -1){
37165                     // cell.title = ddaysText;
37166                    // cell.className = " fc-state-disabled";
37167                 }
37168             }
37169             if(ddMatch && format){
37170                 var fvalue = d.dateFormat(format);
37171                 if(ddMatch.test(fvalue)){
37172                     cell.title = ddText.replace("%0", fvalue);
37173                    cell.className = " fc-state-disabled";
37174                 }
37175             }
37176             
37177             if (!cell.initialClassName) {
37178                 cell.initialClassName = cell.dom.className;
37179             }
37180             
37181             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
37182         };
37183
37184         var i = 0;
37185         
37186         for(; i < startingPos; i++) {
37187             cells[i].dayName =  (++prevStart);
37188             Roo.log(textEls[i]);
37189             d.setDate(d.getDate()+1);
37190             
37191             //cells[i].className = "fc-past fc-other-month";
37192             setCellClass(this, cells[i]);
37193         }
37194         
37195         var intDay = 0;
37196         
37197         for(; i < days; i++){
37198             intDay = i - startingPos + 1;
37199             cells[i].dayName =  (intDay);
37200             d.setDate(d.getDate()+1);
37201             
37202             cells[i].className = ''; // "x-date-active";
37203             setCellClass(this, cells[i]);
37204         }
37205         var extraDays = 0;
37206         
37207         for(; i < 42; i++) {
37208             //textEls[i].innerHTML = (++extraDays);
37209             
37210             d.setDate(d.getDate()+1);
37211             cells[i].dayName = (++extraDays);
37212             cells[i].className = "fc-future fc-other-month";
37213             setCellClass(this, cells[i]);
37214         }
37215         
37216         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
37217         
37218         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
37219         
37220         // this will cause all the cells to mis
37221         var rows= [];
37222         var i =0;
37223         for (var r = 0;r < 6;r++) {
37224             for (var c =0;c < 7;c++) {
37225                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
37226             }    
37227         }
37228         
37229         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37230         for(i=0;i<cells.length;i++) {
37231             
37232             this.cells.elements[i].dayName = cells[i].dayName ;
37233             this.cells.elements[i].className = cells[i].className;
37234             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
37235             this.cells.elements[i].title = cells[i].title ;
37236             this.cells.elements[i].dateValue = cells[i].dateValue ;
37237         }
37238         
37239         
37240         
37241         
37242         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
37243         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
37244         
37245         ////if(totalRows != 6){
37246             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
37247            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
37248        // }
37249         
37250         this.fireEvent('monthchange', this, date);
37251         
37252         
37253     },
37254  /**
37255      * Returns the grid's SelectionModel.
37256      * @return {SelectionModel}
37257      */
37258     getSelectionModel : function(){
37259         if(!this.selModel){
37260             this.selModel = new Roo.grid.CellSelectionModel();
37261         }
37262         return this.selModel;
37263     },
37264
37265     load: function() {
37266         this.eventStore.load()
37267         
37268         
37269         
37270     },
37271     
37272     findCell : function(dt) {
37273         dt = dt.clearTime().getTime();
37274         var ret = false;
37275         this.cells.each(function(c){
37276             //Roo.log("check " +c.dateValue + '?=' + dt);
37277             if(c.dateValue == dt){
37278                 ret = c;
37279                 return false;
37280             }
37281             return true;
37282         });
37283         
37284         return ret;
37285     },
37286     
37287     findCells : function(rec) {
37288         var s = rec.data.start_dt.clone().clearTime().getTime();
37289        // Roo.log(s);
37290         var e= rec.data.end_dt.clone().clearTime().getTime();
37291        // Roo.log(e);
37292         var ret = [];
37293         this.cells.each(function(c){
37294              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
37295             
37296             if(c.dateValue > e){
37297                 return ;
37298             }
37299             if(c.dateValue < s){
37300                 return ;
37301             }
37302             ret.push(c);
37303         });
37304         
37305         return ret;    
37306     },
37307     
37308     findBestRow: function(cells)
37309     {
37310         var ret = 0;
37311         
37312         for (var i =0 ; i < cells.length;i++) {
37313             ret  = Math.max(cells[i].rows || 0,ret);
37314         }
37315         return ret;
37316         
37317     },
37318     
37319     
37320     addItem : function(rec)
37321     {
37322         // look for vertical location slot in
37323         var cells = this.findCells(rec);
37324         
37325         rec.row = this.findBestRow(cells);
37326         
37327         // work out the location.
37328         
37329         var crow = false;
37330         var rows = [];
37331         for(var i =0; i < cells.length; i++) {
37332             if (!crow) {
37333                 crow = {
37334                     start : cells[i],
37335                     end :  cells[i]
37336                 };
37337                 continue;
37338             }
37339             if (crow.start.getY() == cells[i].getY()) {
37340                 // on same row.
37341                 crow.end = cells[i];
37342                 continue;
37343             }
37344             // different row.
37345             rows.push(crow);
37346             crow = {
37347                 start: cells[i],
37348                 end : cells[i]
37349             };
37350             
37351         }
37352         
37353         rows.push(crow);
37354         rec.els = [];
37355         rec.rows = rows;
37356         rec.cells = cells;
37357         for (var i = 0; i < cells.length;i++) {
37358             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
37359             
37360         }
37361         
37362         
37363     },
37364     
37365     clearEvents: function() {
37366         
37367         if (!this.eventStore.getCount()) {
37368             return;
37369         }
37370         // reset number of rows in cells.
37371         Roo.each(this.cells.elements, function(c){
37372             c.rows = 0;
37373         });
37374         
37375         this.eventStore.each(function(e) {
37376             this.clearEvent(e);
37377         },this);
37378         
37379     },
37380     
37381     clearEvent : function(ev)
37382     {
37383         if (ev.els) {
37384             Roo.each(ev.els, function(el) {
37385                 el.un('mouseenter' ,this.onEventEnter, this);
37386                 el.un('mouseleave' ,this.onEventLeave, this);
37387                 el.remove();
37388             },this);
37389             ev.els = [];
37390         }
37391     },
37392     
37393     
37394     renderEvent : function(ev,ctr) {
37395         if (!ctr) {
37396              ctr = this.view.el.select('.fc-event-container',true).first();
37397         }
37398         
37399          
37400         this.clearEvent(ev);
37401             //code
37402        
37403         
37404         
37405         ev.els = [];
37406         var cells = ev.cells;
37407         var rows = ev.rows;
37408         this.fireEvent('eventrender', this, ev);
37409         
37410         for(var i =0; i < rows.length; i++) {
37411             
37412             cls = '';
37413             if (i == 0) {
37414                 cls += ' fc-event-start';
37415             }
37416             if ((i+1) == rows.length) {
37417                 cls += ' fc-event-end';
37418             }
37419             
37420             //Roo.log(ev.data);
37421             // how many rows should it span..
37422             var cg = this.eventTmpl.append(ctr,Roo.apply({
37423                 fccls : cls
37424                 
37425             }, ev.data) , true);
37426             
37427             
37428             cg.on('mouseenter' ,this.onEventEnter, this, ev);
37429             cg.on('mouseleave' ,this.onEventLeave, this, ev);
37430             cg.on('click', this.onEventClick, this, ev);
37431             
37432             ev.els.push(cg);
37433             
37434             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
37435             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
37436             //Roo.log(cg);
37437              
37438             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
37439             cg.setWidth(ebox.right - sbox.x -2);
37440         }
37441     },
37442     
37443     renderEvents: function()
37444     {   
37445         // first make sure there is enough space..
37446         
37447         if (!this.eventTmpl) {
37448             this.eventTmpl = new Roo.Template(
37449                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
37450                     '<div class="fc-event-inner">' +
37451                         '<span class="fc-event-time">{time}</span>' +
37452                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
37453                     '</div>' +
37454                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
37455                 '</div>'
37456             );
37457                 
37458         }
37459                
37460         
37461         
37462         this.cells.each(function(c) {
37463             //Roo.log(c.select('.fc-day-content div',true).first());
37464             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
37465         });
37466         
37467         var ctr = this.view.el.select('.fc-event-container',true).first();
37468         
37469         var cls;
37470         this.eventStore.each(function(ev){
37471             
37472             this.renderEvent(ev);
37473              
37474              
37475         }, this);
37476         this.view.layout();
37477         
37478     },
37479     
37480     onEventEnter: function (e, el,event,d) {
37481         this.fireEvent('evententer', this, el, event);
37482     },
37483     
37484     onEventLeave: function (e, el,event,d) {
37485         this.fireEvent('eventleave', this, el, event);
37486     },
37487     
37488     onEventClick: function (e, el,event,d) {
37489         this.fireEvent('eventclick', this, el, event);
37490     },
37491     
37492     onMonthChange: function () {
37493         this.store.load();
37494     },
37495     
37496     onLoad: function () {
37497         
37498         //Roo.log('calendar onload');
37499 //         
37500         if(this.eventStore.getCount() > 0){
37501             
37502            
37503             
37504             this.eventStore.each(function(d){
37505                 
37506                 
37507                 // FIXME..
37508                 var add =   d.data;
37509                 if (typeof(add.end_dt) == 'undefined')  {
37510                     Roo.log("Missing End time in calendar data: ");
37511                     Roo.log(d);
37512                     return;
37513                 }
37514                 if (typeof(add.start_dt) == 'undefined')  {
37515                     Roo.log("Missing Start time in calendar data: ");
37516                     Roo.log(d);
37517                     return;
37518                 }
37519                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
37520                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
37521                 add.id = add.id || d.id;
37522                 add.title = add.title || '??';
37523                 
37524                 this.addItem(d);
37525                 
37526              
37527             },this);
37528         }
37529         
37530         this.renderEvents();
37531     }
37532     
37533
37534 });
37535 /*
37536  grid : {
37537                 xtype: 'Grid',
37538                 xns: Roo.grid,
37539                 listeners : {
37540                     render : function ()
37541                     {
37542                         _this.grid = this;
37543                         
37544                         if (!this.view.el.hasClass('course-timesheet')) {
37545                             this.view.el.addClass('course-timesheet');
37546                         }
37547                         if (this.tsStyle) {
37548                             this.ds.load({});
37549                             return; 
37550                         }
37551                         Roo.log('width');
37552                         Roo.log(_this.grid.view.el.getWidth());
37553                         
37554                         
37555                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
37556                             '.course-timesheet .x-grid-row' : {
37557                                 height: '80px'
37558                             },
37559                             '.x-grid-row td' : {
37560                                 'vertical-align' : 0
37561                             },
37562                             '.course-edit-link' : {
37563                                 'color' : 'blue',
37564                                 'text-overflow' : 'ellipsis',
37565                                 'overflow' : 'hidden',
37566                                 'white-space' : 'nowrap',
37567                                 'cursor' : 'pointer'
37568                             },
37569                             '.sub-link' : {
37570                                 'color' : 'green'
37571                             },
37572                             '.de-act-sup-link' : {
37573                                 'color' : 'purple',
37574                                 'text-decoration' : 'line-through'
37575                             },
37576                             '.de-act-link' : {
37577                                 'color' : 'red',
37578                                 'text-decoration' : 'line-through'
37579                             },
37580                             '.course-timesheet .course-highlight' : {
37581                                 'border-top-style': 'dashed !important',
37582                                 'border-bottom-bottom': 'dashed !important'
37583                             },
37584                             '.course-timesheet .course-item' : {
37585                                 'font-family'   : 'tahoma, arial, helvetica',
37586                                 'font-size'     : '11px',
37587                                 'overflow'      : 'hidden',
37588                                 'padding-left'  : '10px',
37589                                 'padding-right' : '10px',
37590                                 'padding-top' : '10px' 
37591                             }
37592                             
37593                         }, Roo.id());
37594                                 this.ds.load({});
37595                     }
37596                 },
37597                 autoWidth : true,
37598                 monitorWindowResize : false,
37599                 cellrenderer : function(v,x,r)
37600                 {
37601                     return v;
37602                 },
37603                 sm : {
37604                     xtype: 'CellSelectionModel',
37605                     xns: Roo.grid
37606                 },
37607                 dataSource : {
37608                     xtype: 'Store',
37609                     xns: Roo.data,
37610                     listeners : {
37611                         beforeload : function (_self, options)
37612                         {
37613                             options.params = options.params || {};
37614                             options.params._month = _this.monthField.getValue();
37615                             options.params.limit = 9999;
37616                             options.params['sort'] = 'when_dt';    
37617                             options.params['dir'] = 'ASC';    
37618                             this.proxy.loadResponse = this.loadResponse;
37619                             Roo.log("load?");
37620                             //this.addColumns();
37621                         },
37622                         load : function (_self, records, options)
37623                         {
37624                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
37625                                 // if you click on the translation.. you can edit it...
37626                                 var el = Roo.get(this);
37627                                 var id = el.dom.getAttribute('data-id');
37628                                 var d = el.dom.getAttribute('data-date');
37629                                 var t = el.dom.getAttribute('data-time');
37630                                 //var id = this.child('span').dom.textContent;
37631                                 
37632                                 //Roo.log(this);
37633                                 Pman.Dialog.CourseCalendar.show({
37634                                     id : id,
37635                                     when_d : d,
37636                                     when_t : t,
37637                                     productitem_active : id ? 1 : 0
37638                                 }, function() {
37639                                     _this.grid.ds.load({});
37640                                 });
37641                            
37642                            });
37643                            
37644                            _this.panel.fireEvent('resize', [ '', '' ]);
37645                         }
37646                     },
37647                     loadResponse : function(o, success, response){
37648                             // this is overridden on before load..
37649                             
37650                             Roo.log("our code?");       
37651                             //Roo.log(success);
37652                             //Roo.log(response)
37653                             delete this.activeRequest;
37654                             if(!success){
37655                                 this.fireEvent("loadexception", this, o, response);
37656                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
37657                                 return;
37658                             }
37659                             var result;
37660                             try {
37661                                 result = o.reader.read(response);
37662                             }catch(e){
37663                                 Roo.log("load exception?");
37664                                 this.fireEvent("loadexception", this, o, response, e);
37665                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
37666                                 return;
37667                             }
37668                             Roo.log("ready...");        
37669                             // loop through result.records;
37670                             // and set this.tdate[date] = [] << array of records..
37671                             _this.tdata  = {};
37672                             Roo.each(result.records, function(r){
37673                                 //Roo.log(r.data);
37674                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
37675                                     _this.tdata[r.data.when_dt.format('j')] = [];
37676                                 }
37677                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
37678                             });
37679                             
37680                             //Roo.log(_this.tdata);
37681                             
37682                             result.records = [];
37683                             result.totalRecords = 6;
37684                     
37685                             // let's generate some duumy records for the rows.
37686                             //var st = _this.dateField.getValue();
37687                             
37688                             // work out monday..
37689                             //st = st.add(Date.DAY, -1 * st.format('w'));
37690                             
37691                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
37692                             
37693                             var firstOfMonth = date.getFirstDayOfMonth();
37694                             var days = date.getDaysInMonth();
37695                             var d = 1;
37696                             var firstAdded = false;
37697                             for (var i = 0; i < result.totalRecords ; i++) {
37698                                 //var d= st.add(Date.DAY, i);
37699                                 var row = {};
37700                                 var added = 0;
37701                                 for(var w = 0 ; w < 7 ; w++){
37702                                     if(!firstAdded && firstOfMonth != w){
37703                                         continue;
37704                                     }
37705                                     if(d > days){
37706                                         continue;
37707                                     }
37708                                     firstAdded = true;
37709                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
37710                                     row['weekday'+w] = String.format(
37711                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
37712                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
37713                                                     d,
37714                                                     date.format('Y-m-')+dd
37715                                                 );
37716                                     added++;
37717                                     if(typeof(_this.tdata[d]) != 'undefined'){
37718                                         Roo.each(_this.tdata[d], function(r){
37719                                             var is_sub = '';
37720                                             var deactive = '';
37721                                             var id = r.id;
37722                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
37723                                             if(r.parent_id*1>0){
37724                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
37725                                                 id = r.parent_id;
37726                                             }
37727                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
37728                                                 deactive = 'de-act-link';
37729                                             }
37730                                             
37731                                             row['weekday'+w] += String.format(
37732                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
37733                                                     id, //0
37734                                                     r.product_id_name, //1
37735                                                     r.when_dt.format('h:ia'), //2
37736                                                     is_sub, //3
37737                                                     deactive, //4
37738                                                     desc // 5
37739                                             );
37740                                         });
37741                                     }
37742                                     d++;
37743                                 }
37744                                 
37745                                 // only do this if something added..
37746                                 if(added > 0){ 
37747                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
37748                                 }
37749                                 
37750                                 
37751                                 // push it twice. (second one with an hour..
37752                                 
37753                             }
37754                             //Roo.log(result);
37755                             this.fireEvent("load", this, o, o.request.arg);
37756                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
37757                         },
37758                     sortInfo : {field: 'when_dt', direction : 'ASC' },
37759                     proxy : {
37760                         xtype: 'HttpProxy',
37761                         xns: Roo.data,
37762                         method : 'GET',
37763                         url : baseURL + '/Roo/Shop_course.php'
37764                     },
37765                     reader : {
37766                         xtype: 'JsonReader',
37767                         xns: Roo.data,
37768                         id : 'id',
37769                         fields : [
37770                             {
37771                                 'name': 'id',
37772                                 'type': 'int'
37773                             },
37774                             {
37775                                 'name': 'when_dt',
37776                                 'type': 'string'
37777                             },
37778                             {
37779                                 'name': 'end_dt',
37780                                 'type': 'string'
37781                             },
37782                             {
37783                                 'name': 'parent_id',
37784                                 'type': 'int'
37785                             },
37786                             {
37787                                 'name': 'product_id',
37788                                 'type': 'int'
37789                             },
37790                             {
37791                                 'name': 'productitem_id',
37792                                 'type': 'int'
37793                             },
37794                             {
37795                                 'name': 'guid',
37796                                 'type': 'int'
37797                             }
37798                         ]
37799                     }
37800                 },
37801                 toolbar : {
37802                     xtype: 'Toolbar',
37803                     xns: Roo,
37804                     items : [
37805                         {
37806                             xtype: 'Button',
37807                             xns: Roo.Toolbar,
37808                             listeners : {
37809                                 click : function (_self, e)
37810                                 {
37811                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
37812                                     sd.setMonth(sd.getMonth()-1);
37813                                     _this.monthField.setValue(sd.format('Y-m-d'));
37814                                     _this.grid.ds.load({});
37815                                 }
37816                             },
37817                             text : "Back"
37818                         },
37819                         {
37820                             xtype: 'Separator',
37821                             xns: Roo.Toolbar
37822                         },
37823                         {
37824                             xtype: 'MonthField',
37825                             xns: Roo.form,
37826                             listeners : {
37827                                 render : function (_self)
37828                                 {
37829                                     _this.monthField = _self;
37830                                    // _this.monthField.set  today
37831                                 },
37832                                 select : function (combo, date)
37833                                 {
37834                                     _this.grid.ds.load({});
37835                                 }
37836                             },
37837                             value : (function() { return new Date(); })()
37838                         },
37839                         {
37840                             xtype: 'Separator',
37841                             xns: Roo.Toolbar
37842                         },
37843                         {
37844                             xtype: 'TextItem',
37845                             xns: Roo.Toolbar,
37846                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
37847                         },
37848                         {
37849                             xtype: 'Fill',
37850                             xns: Roo.Toolbar
37851                         },
37852                         {
37853                             xtype: 'Button',
37854                             xns: Roo.Toolbar,
37855                             listeners : {
37856                                 click : function (_self, e)
37857                                 {
37858                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
37859                                     sd.setMonth(sd.getMonth()+1);
37860                                     _this.monthField.setValue(sd.format('Y-m-d'));
37861                                     _this.grid.ds.load({});
37862                                 }
37863                             },
37864                             text : "Next"
37865                         }
37866                     ]
37867                 },
37868                  
37869             }
37870         };
37871         
37872         *//*
37873  * Based on:
37874  * Ext JS Library 1.1.1
37875  * Copyright(c) 2006-2007, Ext JS, LLC.
37876  *
37877  * Originally Released Under LGPL - original licence link has changed is not relivant.
37878  *
37879  * Fork - LGPL
37880  * <script type="text/javascript">
37881  */
37882  
37883 /**
37884  * @class Roo.LoadMask
37885  * A simple utility class for generically masking elements while loading data.  If the element being masked has
37886  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
37887  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
37888  * element's UpdateManager load indicator and will be destroyed after the initial load.
37889  * @constructor
37890  * Create a new LoadMask
37891  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
37892  * @param {Object} config The config object
37893  */
37894 Roo.LoadMask = function(el, config){
37895     this.el = Roo.get(el);
37896     Roo.apply(this, config);
37897     if(this.store){
37898         this.store.on('beforeload', this.onBeforeLoad, this);
37899         this.store.on('load', this.onLoad, this);
37900         this.store.on('loadexception', this.onLoadException, this);
37901         this.removeMask = false;
37902     }else{
37903         var um = this.el.getUpdateManager();
37904         um.showLoadIndicator = false; // disable the default indicator
37905         um.on('beforeupdate', this.onBeforeLoad, this);
37906         um.on('update', this.onLoad, this);
37907         um.on('failure', this.onLoad, this);
37908         this.removeMask = true;
37909     }
37910 };
37911
37912 Roo.LoadMask.prototype = {
37913     /**
37914      * @cfg {Boolean} removeMask
37915      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
37916      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
37917      */
37918     /**
37919      * @cfg {String} msg
37920      * The text to display in a centered loading message box (defaults to 'Loading...')
37921      */
37922     msg : 'Loading...',
37923     /**
37924      * @cfg {String} msgCls
37925      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
37926      */
37927     msgCls : 'x-mask-loading',
37928
37929     /**
37930      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
37931      * @type Boolean
37932      */
37933     disabled: false,
37934
37935     /**
37936      * Disables the mask to prevent it from being displayed
37937      */
37938     disable : function(){
37939        this.disabled = true;
37940     },
37941
37942     /**
37943      * Enables the mask so that it can be displayed
37944      */
37945     enable : function(){
37946         this.disabled = false;
37947     },
37948     
37949     onLoadException : function()
37950     {
37951         Roo.log(arguments);
37952         
37953         if (typeof(arguments[3]) != 'undefined') {
37954             Roo.MessageBox.alert("Error loading",arguments[3]);
37955         } 
37956         /*
37957         try {
37958             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
37959                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
37960             }   
37961         } catch(e) {
37962             
37963         }
37964         */
37965     
37966         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
37967     },
37968     // private
37969     onLoad : function()
37970     {
37971         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
37972     },
37973
37974     // private
37975     onBeforeLoad : function(){
37976         if(!this.disabled){
37977             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
37978         }
37979     },
37980
37981     // private
37982     destroy : function(){
37983         if(this.store){
37984             this.store.un('beforeload', this.onBeforeLoad, this);
37985             this.store.un('load', this.onLoad, this);
37986             this.store.un('loadexception', this.onLoadException, this);
37987         }else{
37988             var um = this.el.getUpdateManager();
37989             um.un('beforeupdate', this.onBeforeLoad, this);
37990             um.un('update', this.onLoad, this);
37991             um.un('failure', this.onLoad, this);
37992         }
37993     }
37994 };/*
37995  * Based on:
37996  * Ext JS Library 1.1.1
37997  * Copyright(c) 2006-2007, Ext JS, LLC.
37998  *
37999  * Originally Released Under LGPL - original licence link has changed is not relivant.
38000  *
38001  * Fork - LGPL
38002  * <script type="text/javascript">
38003  */
38004
38005
38006 /**
38007  * @class Roo.XTemplate
38008  * @extends Roo.Template
38009  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
38010 <pre><code>
38011 var t = new Roo.XTemplate(
38012         '&lt;select name="{name}"&gt;',
38013                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
38014         '&lt;/select&gt;'
38015 );
38016  
38017 // then append, applying the master template values
38018  </code></pre>
38019  *
38020  * Supported features:
38021  *
38022  *  Tags:
38023
38024 <pre><code>
38025       {a_variable} - output encoded.
38026       {a_variable.format:("Y-m-d")} - call a method on the variable
38027       {a_variable:raw} - unencoded output
38028       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
38029       {a_variable:this.method_on_template(...)} - call a method on the template object.
38030  
38031 </code></pre>
38032  *  The tpl tag:
38033 <pre><code>
38034         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
38035         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
38036         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
38037         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
38038   
38039         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
38040         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
38041 </code></pre>
38042  *      
38043  */
38044 Roo.XTemplate = function()
38045 {
38046     Roo.XTemplate.superclass.constructor.apply(this, arguments);
38047     if (this.html) {
38048         this.compile();
38049     }
38050 };
38051
38052
38053 Roo.extend(Roo.XTemplate, Roo.Template, {
38054
38055     /**
38056      * The various sub templates
38057      */
38058     tpls : false,
38059     /**
38060      *
38061      * basic tag replacing syntax
38062      * WORD:WORD()
38063      *
38064      * // you can fake an object call by doing this
38065      *  x.t:(test,tesT) 
38066      * 
38067      */
38068     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
38069
38070     /**
38071      * compile the template
38072      *
38073      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
38074      *
38075      */
38076     compile: function()
38077     {
38078         var s = this.html;
38079      
38080         s = ['<tpl>', s, '</tpl>'].join('');
38081     
38082         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
38083             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
38084             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
38085             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
38086             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
38087             m,
38088             id     = 0,
38089             tpls   = [];
38090     
38091         while(true == !!(m = s.match(re))){
38092             var forMatch   = m[0].match(nameRe),
38093                 ifMatch   = m[0].match(ifRe),
38094                 execMatch   = m[0].match(execRe),
38095                 namedMatch   = m[0].match(namedRe),
38096                 
38097                 exp  = null, 
38098                 fn   = null,
38099                 exec = null,
38100                 name = forMatch && forMatch[1] ? forMatch[1] : '';
38101                 
38102             if (ifMatch) {
38103                 // if - puts fn into test..
38104                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
38105                 if(exp){
38106                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
38107                 }
38108             }
38109             
38110             if (execMatch) {
38111                 // exec - calls a function... returns empty if true is  returned.
38112                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
38113                 if(exp){
38114                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
38115                 }
38116             }
38117             
38118             
38119             if (name) {
38120                 // for = 
38121                 switch(name){
38122                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
38123                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
38124                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
38125                 }
38126             }
38127             var uid = namedMatch ? namedMatch[1] : id;
38128             
38129             
38130             tpls.push({
38131                 id:     namedMatch ? namedMatch[1] : id,
38132                 target: name,
38133                 exec:   exec,
38134                 test:   fn,
38135                 body:   m[1] || ''
38136             });
38137             if (namedMatch) {
38138                 s = s.replace(m[0], '');
38139             } else { 
38140                 s = s.replace(m[0], '{xtpl'+ id + '}');
38141             }
38142             ++id;
38143         }
38144         this.tpls = [];
38145         for(var i = tpls.length-1; i >= 0; --i){
38146             this.compileTpl(tpls[i]);
38147             this.tpls[tpls[i].id] = tpls[i];
38148         }
38149         this.master = tpls[tpls.length-1];
38150         return this;
38151     },
38152     /**
38153      * same as applyTemplate, except it's done to one of the subTemplates
38154      * when using named templates, you can do:
38155      *
38156      * var str = pl.applySubTemplate('your-name', values);
38157      *
38158      * 
38159      * @param {Number} id of the template
38160      * @param {Object} values to apply to template
38161      * @param {Object} parent (normaly the instance of this object)
38162      */
38163     applySubTemplate : function(id, values, parent)
38164     {
38165         
38166         
38167         var t = this.tpls[id];
38168         
38169         
38170         try { 
38171             if(t.test && !t.test.call(this, values, parent)){
38172                 return '';
38173             }
38174         } catch(e) {
38175             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
38176             Roo.log(e.toString());
38177             Roo.log(t.test);
38178             return ''
38179         }
38180         try { 
38181             
38182             if(t.exec && t.exec.call(this, values, parent)){
38183                 return '';
38184             }
38185         } catch(e) {
38186             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
38187             Roo.log(e.toString());
38188             Roo.log(t.exec);
38189             return ''
38190         }
38191         try {
38192             var vs = t.target ? t.target.call(this, values, parent) : values;
38193             parent = t.target ? values : parent;
38194             if(t.target && vs instanceof Array){
38195                 var buf = [];
38196                 for(var i = 0, len = vs.length; i < len; i++){
38197                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
38198                 }
38199                 return buf.join('');
38200             }
38201             return t.compiled.call(this, vs, parent);
38202         } catch (e) {
38203             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
38204             Roo.log(e.toString());
38205             Roo.log(t.compiled);
38206             return '';
38207         }
38208     },
38209
38210     compileTpl : function(tpl)
38211     {
38212         var fm = Roo.util.Format;
38213         var useF = this.disableFormats !== true;
38214         var sep = Roo.isGecko ? "+" : ",";
38215         var undef = function(str) {
38216             Roo.log("Property not found :"  + str);
38217             return '';
38218         };
38219         
38220         var fn = function(m, name, format, args)
38221         {
38222             //Roo.log(arguments);
38223             args = args ? args.replace(/\\'/g,"'") : args;
38224             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
38225             if (typeof(format) == 'undefined') {
38226                 format= 'htmlEncode';
38227             }
38228             if (format == 'raw' ) {
38229                 format = false;
38230             }
38231             
38232             if(name.substr(0, 4) == 'xtpl'){
38233                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
38234             }
38235             
38236             // build an array of options to determine if value is undefined..
38237             
38238             // basically get 'xxxx.yyyy' then do
38239             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
38240             //    (function () { Roo.log("Property not found"); return ''; })() :
38241             //    ......
38242             
38243             var udef_ar = [];
38244             var lookfor = '';
38245             Roo.each(name.split('.'), function(st) {
38246                 lookfor += (lookfor.length ? '.': '') + st;
38247                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
38248             });
38249             
38250             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
38251             
38252             
38253             if(format && useF){
38254                 
38255                 args = args ? ',' + args : "";
38256                  
38257                 if(format.substr(0, 5) != "this."){
38258                     format = "fm." + format + '(';
38259                 }else{
38260                     format = 'this.call("'+ format.substr(5) + '", ';
38261                     args = ", values";
38262                 }
38263                 
38264                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
38265             }
38266              
38267             if (args.length) {
38268                 // called with xxyx.yuu:(test,test)
38269                 // change to ()
38270                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
38271             }
38272             // raw.. - :raw modifier..
38273             return "'"+ sep + udef_st  + name + ")"+sep+"'";
38274             
38275         };
38276         var body;
38277         // branched to use + in gecko and [].join() in others
38278         if(Roo.isGecko){
38279             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
38280                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
38281                     "';};};";
38282         }else{
38283             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
38284             body.push(tpl.body.replace(/(\r\n|\n)/g,
38285                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
38286             body.push("'].join('');};};");
38287             body = body.join('');
38288         }
38289         
38290         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
38291        
38292         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
38293         eval(body);
38294         
38295         return this;
38296     },
38297
38298     applyTemplate : function(values){
38299         return this.master.compiled.call(this, values, {});
38300         //var s = this.subs;
38301     },
38302
38303     apply : function(){
38304         return this.applyTemplate.apply(this, arguments);
38305     }
38306
38307  });
38308
38309 Roo.XTemplate.from = function(el){
38310     el = Roo.getDom(el);
38311     return new Roo.XTemplate(el.value || el.innerHTML);
38312 };