roojs-ui.js
[roojs1] / roojs-ui-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11
12
13 /**
14  * @class Roo.data.SortTypes
15  * @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         Roo.log('tree.js appendChild');
2549         var multi = false;
2550         if(node instanceof Array){
2551             multi = node;
2552         }else if(arguments.length > 1){
2553             multi = arguments;
2554         }
2555         // if passed an array or multiple args do them one by one
2556         if(multi){
2557             for(var i = 0, len = multi.length; i < len; i++) {
2558                 this.appendChild(multi[i]);
2559             }
2560         }else{
2561             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
2562                 return false;
2563             }
2564             var index = this.childNodes.length;
2565             var oldParent = node.parentNode;
2566             // it's a move, make sure we move it cleanly
2567             if(oldParent){
2568                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
2569                     return false;
2570                 }
2571                 oldParent.removeChild(node);
2572             }
2573             index = this.childNodes.length;
2574             if(index == 0){
2575                 this.setFirstChild(node);
2576             }
2577             this.childNodes.push(node);
2578             node.parentNode = this;
2579             var ps = this.childNodes[index-1];
2580             if(ps){
2581                 node.previousSibling = ps;
2582                 ps.nextSibling = node;
2583             }else{
2584                 node.previousSibling = null;
2585             }
2586             node.nextSibling = null;
2587             this.setLastChild(node);
2588             node.setOwnerTree(this.getOwnerTree());
2589             this.fireEvent("append", this.ownerTree, this, node, index);
2590             if(oldParent){
2591                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
2592             }
2593             return node;
2594         }
2595     },
2596
2597     /**
2598      * Removes a child node from this node.
2599      * @param {Node} node The node to remove
2600      * @return {Node} The removed node
2601      */
2602     removeChild : function(node){
2603         var index = this.childNodes.indexOf(node);
2604         if(index == -1){
2605             return false;
2606         }
2607         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
2608             return false;
2609         }
2610
2611         // remove it from childNodes collection
2612         this.childNodes.splice(index, 1);
2613
2614         // update siblings
2615         if(node.previousSibling){
2616             node.previousSibling.nextSibling = node.nextSibling;
2617         }
2618         if(node.nextSibling){
2619             node.nextSibling.previousSibling = node.previousSibling;
2620         }
2621
2622         // update child refs
2623         if(this.firstChild == node){
2624             this.setFirstChild(node.nextSibling);
2625         }
2626         if(this.lastChild == node){
2627             this.setLastChild(node.previousSibling);
2628         }
2629
2630         node.setOwnerTree(null);
2631         // clear any references from the node
2632         node.parentNode = null;
2633         node.previousSibling = null;
2634         node.nextSibling = null;
2635         this.fireEvent("remove", this.ownerTree, this, node);
2636         return node;
2637     },
2638
2639     /**
2640      * Inserts the first node before the second node in this nodes childNodes collection.
2641      * @param {Node} node The node to insert
2642      * @param {Node} refNode The node to insert before (if null the node is appended)
2643      * @return {Node} The inserted node
2644      */
2645     insertBefore : function(node, refNode){
2646         if(!refNode){ // like standard Dom, refNode can be null for append
2647             return this.appendChild(node);
2648         }
2649         // nothing to do
2650         if(node == refNode){
2651             return false;
2652         }
2653
2654         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
2655             return false;
2656         }
2657         var index = this.childNodes.indexOf(refNode);
2658         var oldParent = node.parentNode;
2659         var refIndex = index;
2660
2661         // when moving internally, indexes will change after remove
2662         if(oldParent == this && this.childNodes.indexOf(node) < index){
2663             refIndex--;
2664         }
2665
2666         // it's a move, make sure we move it cleanly
2667         if(oldParent){
2668             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
2669                 return false;
2670             }
2671             oldParent.removeChild(node);
2672         }
2673         if(refIndex == 0){
2674             this.setFirstChild(node);
2675         }
2676         this.childNodes.splice(refIndex, 0, node);
2677         node.parentNode = this;
2678         var ps = this.childNodes[refIndex-1];
2679         if(ps){
2680             node.previousSibling = ps;
2681             ps.nextSibling = node;
2682         }else{
2683             node.previousSibling = null;
2684         }
2685         node.nextSibling = refNode;
2686         refNode.previousSibling = node;
2687         node.setOwnerTree(this.getOwnerTree());
2688         this.fireEvent("insert", this.ownerTree, this, node, refNode);
2689         if(oldParent){
2690             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
2691         }
2692         return node;
2693     },
2694
2695     /**
2696      * Returns the child node at the specified index.
2697      * @param {Number} index
2698      * @return {Node}
2699      */
2700     item : function(index){
2701         return this.childNodes[index];
2702     },
2703
2704     /**
2705      * Replaces one child node in this node with another.
2706      * @param {Node} newChild The replacement node
2707      * @param {Node} oldChild The node to replace
2708      * @return {Node} The replaced node
2709      */
2710     replaceChild : function(newChild, oldChild){
2711         this.insertBefore(newChild, oldChild);
2712         this.removeChild(oldChild);
2713         return oldChild;
2714     },
2715
2716     /**
2717      * Returns the index of a child node
2718      * @param {Node} node
2719      * @return {Number} The index of the node or -1 if it was not found
2720      */
2721     indexOf : function(child){
2722         return this.childNodes.indexOf(child);
2723     },
2724
2725     /**
2726      * Returns the tree this node is in.
2727      * @return {Tree}
2728      */
2729     getOwnerTree : function(){
2730         // if it doesn't have one, look for one
2731         if(!this.ownerTree){
2732             var p = this;
2733             while(p){
2734                 if(p.ownerTree){
2735                     this.ownerTree = p.ownerTree;
2736                     break;
2737                 }
2738                 p = p.parentNode;
2739             }
2740         }
2741         return this.ownerTree;
2742     },
2743
2744     /**
2745      * Returns depth of this node (the root node has a depth of 0)
2746      * @return {Number}
2747      */
2748     getDepth : function(){
2749         var depth = 0;
2750         var p = this;
2751         while(p.parentNode){
2752             ++depth;
2753             p = p.parentNode;
2754         }
2755         return depth;
2756     },
2757
2758     // private
2759     setOwnerTree : function(tree){
2760         // if it's move, we need to update everyone
2761         if(tree != this.ownerTree){
2762             if(this.ownerTree){
2763                 this.ownerTree.unregisterNode(this);
2764             }
2765             this.ownerTree = tree;
2766             var cs = this.childNodes;
2767             for(var i = 0, len = cs.length; i < len; i++) {
2768                 cs[i].setOwnerTree(tree);
2769             }
2770             if(tree){
2771                 tree.registerNode(this);
2772             }
2773         }
2774     },
2775
2776     /**
2777      * Returns the path for this node. The path can be used to expand or select this node programmatically.
2778      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
2779      * @return {String} The path
2780      */
2781     getPath : function(attr){
2782         attr = attr || "id";
2783         var p = this.parentNode;
2784         var b = [this.attributes[attr]];
2785         while(p){
2786             b.unshift(p.attributes[attr]);
2787             p = p.parentNode;
2788         }
2789         var sep = this.getOwnerTree().pathSeparator;
2790         return sep + b.join(sep);
2791     },
2792
2793     /**
2794      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2795      * function call will be the scope provided or the current node. The arguments to the function
2796      * will be the args provided or the current node. If the function returns false at any point,
2797      * the bubble is stopped.
2798      * @param {Function} fn The function to call
2799      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2800      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2801      */
2802     bubble : function(fn, scope, args){
2803         var p = this;
2804         while(p){
2805             if(fn.call(scope || p, args || p) === false){
2806                 break;
2807             }
2808             p = p.parentNode;
2809         }
2810     },
2811
2812     /**
2813      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2814      * function call will be the scope provided or the current node. The arguments to the function
2815      * will be the args provided or the current node. If the function returns false at any point,
2816      * the cascade is stopped on that branch.
2817      * @param {Function} fn The function to call
2818      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2819      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2820      */
2821     cascade : function(fn, scope, args){
2822         if(fn.call(scope || this, args || this) !== false){
2823             var cs = this.childNodes;
2824             for(var i = 0, len = cs.length; i < len; i++) {
2825                 cs[i].cascade(fn, scope, args);
2826             }
2827         }
2828     },
2829
2830     /**
2831      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
2832      * function call will be the scope provided or the current node. The arguments to the function
2833      * will be the args provided or the current node. If the function returns false at any point,
2834      * the iteration stops.
2835      * @param {Function} fn The function to call
2836      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2837      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2838      */
2839     eachChild : function(fn, scope, args){
2840         var cs = this.childNodes;
2841         for(var i = 0, len = cs.length; i < len; i++) {
2842                 if(fn.call(scope || this, args || cs[i]) === false){
2843                     break;
2844                 }
2845         }
2846     },
2847
2848     /**
2849      * Finds the first child that has the attribute with the specified value.
2850      * @param {String} attribute The attribute name
2851      * @param {Mixed} value The value to search for
2852      * @return {Node} The found child or null if none was found
2853      */
2854     findChild : function(attribute, value){
2855         var cs = this.childNodes;
2856         for(var i = 0, len = cs.length; i < len; i++) {
2857                 if(cs[i].attributes[attribute] == value){
2858                     return cs[i];
2859                 }
2860         }
2861         return null;
2862     },
2863
2864     /**
2865      * Finds the first child by a custom function. The child matches if the function passed
2866      * returns true.
2867      * @param {Function} fn
2868      * @param {Object} scope (optional)
2869      * @return {Node} The found child or null if none was found
2870      */
2871     findChildBy : function(fn, scope){
2872         var cs = this.childNodes;
2873         for(var i = 0, len = cs.length; i < len; i++) {
2874                 if(fn.call(scope||cs[i], cs[i]) === true){
2875                     return cs[i];
2876                 }
2877         }
2878         return null;
2879     },
2880
2881     /**
2882      * Sorts this nodes children using the supplied sort function
2883      * @param {Function} fn
2884      * @param {Object} scope (optional)
2885      */
2886     sort : function(fn, scope){
2887         var cs = this.childNodes;
2888         var len = cs.length;
2889         if(len > 0){
2890             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
2891             cs.sort(sortFn);
2892             for(var i = 0; i < len; i++){
2893                 var n = cs[i];
2894                 n.previousSibling = cs[i-1];
2895                 n.nextSibling = cs[i+1];
2896                 if(i == 0){
2897                     this.setFirstChild(n);
2898                 }
2899                 if(i == len-1){
2900                     this.setLastChild(n);
2901                 }
2902             }
2903         }
2904     },
2905
2906     /**
2907      * Returns true if this node is an ancestor (at any point) of the passed node.
2908      * @param {Node} node
2909      * @return {Boolean}
2910      */
2911     contains : function(node){
2912         return node.isAncestor(this);
2913     },
2914
2915     /**
2916      * Returns true if the passed node is an ancestor (at any point) of this node.
2917      * @param {Node} node
2918      * @return {Boolean}
2919      */
2920     isAncestor : function(node){
2921         var p = this.parentNode;
2922         while(p){
2923             if(p == node){
2924                 return true;
2925             }
2926             p = p.parentNode;
2927         }
2928         return false;
2929     },
2930
2931     toString : function(){
2932         return "[Node"+(this.id?" "+this.id:"")+"]";
2933     }
2934 });/*
2935  * Based on:
2936  * Ext JS Library 1.1.1
2937  * Copyright(c) 2006-2007, Ext JS, LLC.
2938  *
2939  * Originally Released Under LGPL - original licence link has changed is not relivant.
2940  *
2941  * Fork - LGPL
2942  * <script type="text/javascript">
2943  */
2944  (function(){ 
2945 /**
2946  * @class Roo.Layer
2947  * @extends Roo.Element
2948  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
2949  * automatic maintaining of shadow/shim positions.
2950  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
2951  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
2952  * you can pass a string with a CSS class name. False turns off the shadow.
2953  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
2954  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
2955  * @cfg {String} cls CSS class to add to the element
2956  * @cfg {Number} zindex Starting z-index (defaults to 11000)
2957  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
2958  * @constructor
2959  * @param {Object} config An object with config options.
2960  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
2961  */
2962
2963 Roo.Layer = function(config, existingEl){
2964     config = config || {};
2965     var dh = Roo.DomHelper;
2966     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
2967     if(existingEl){
2968         this.dom = Roo.getDom(existingEl);
2969     }
2970     if(!this.dom){
2971         var o = config.dh || {tag: "div", cls: "x-layer"};
2972         this.dom = dh.append(pel, o);
2973     }
2974     if(config.cls){
2975         this.addClass(config.cls);
2976     }
2977     this.constrain = config.constrain !== false;
2978     this.visibilityMode = Roo.Element.VISIBILITY;
2979     if(config.id){
2980         this.id = this.dom.id = config.id;
2981     }else{
2982         this.id = Roo.id(this.dom);
2983     }
2984     this.zindex = config.zindex || this.getZIndex();
2985     this.position("absolute", this.zindex);
2986     if(config.shadow){
2987         this.shadowOffset = config.shadowOffset || 4;
2988         this.shadow = new Roo.Shadow({
2989             offset : this.shadowOffset,
2990             mode : config.shadow
2991         });
2992     }else{
2993         this.shadowOffset = 0;
2994     }
2995     this.useShim = config.shim !== false && Roo.useShims;
2996     this.useDisplay = config.useDisplay;
2997     this.hide();
2998 };
2999
3000 var supr = Roo.Element.prototype;
3001
3002 // shims are shared among layer to keep from having 100 iframes
3003 var shims = [];
3004
3005 Roo.extend(Roo.Layer, Roo.Element, {
3006
3007     getZIndex : function(){
3008         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
3009     },
3010
3011     getShim : function(){
3012         if(!this.useShim){
3013             return null;
3014         }
3015         if(this.shim){
3016             return this.shim;
3017         }
3018         var shim = shims.shift();
3019         if(!shim){
3020             shim = this.createShim();
3021             shim.enableDisplayMode('block');
3022             shim.dom.style.display = 'none';
3023             shim.dom.style.visibility = 'visible';
3024         }
3025         var pn = this.dom.parentNode;
3026         if(shim.dom.parentNode != pn){
3027             pn.insertBefore(shim.dom, this.dom);
3028         }
3029         shim.setStyle('z-index', this.getZIndex()-2);
3030         this.shim = shim;
3031         return shim;
3032     },
3033
3034     hideShim : function(){
3035         if(this.shim){
3036             this.shim.setDisplayed(false);
3037             shims.push(this.shim);
3038             delete this.shim;
3039         }
3040     },
3041
3042     disableShadow : function(){
3043         if(this.shadow){
3044             this.shadowDisabled = true;
3045             this.shadow.hide();
3046             this.lastShadowOffset = this.shadowOffset;
3047             this.shadowOffset = 0;
3048         }
3049     },
3050
3051     enableShadow : function(show){
3052         if(this.shadow){
3053             this.shadowDisabled = false;
3054             this.shadowOffset = this.lastShadowOffset;
3055             delete this.lastShadowOffset;
3056             if(show){
3057                 this.sync(true);
3058             }
3059         }
3060     },
3061
3062     // private
3063     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
3064     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
3065     sync : function(doShow){
3066         var sw = this.shadow;
3067         if(!this.updating && this.isVisible() && (sw || this.useShim)){
3068             var sh = this.getShim();
3069
3070             var w = this.getWidth(),
3071                 h = this.getHeight();
3072
3073             var l = this.getLeft(true),
3074                 t = this.getTop(true);
3075
3076             if(sw && !this.shadowDisabled){
3077                 if(doShow && !sw.isVisible()){
3078                     sw.show(this);
3079                 }else{
3080                     sw.realign(l, t, w, h);
3081                 }
3082                 if(sh){
3083                     if(doShow){
3084                        sh.show();
3085                     }
3086                     // fit the shim behind the shadow, so it is shimmed too
3087                     var a = sw.adjusts, s = sh.dom.style;
3088                     s.left = (Math.min(l, l+a.l))+"px";
3089                     s.top = (Math.min(t, t+a.t))+"px";
3090                     s.width = (w+a.w)+"px";
3091                     s.height = (h+a.h)+"px";
3092                 }
3093             }else if(sh){
3094                 if(doShow){
3095                    sh.show();
3096                 }
3097                 sh.setSize(w, h);
3098                 sh.setLeftTop(l, t);
3099             }
3100             
3101         }
3102     },
3103
3104     // private
3105     destroy : function(){
3106         this.hideShim();
3107         if(this.shadow){
3108             this.shadow.hide();
3109         }
3110         this.removeAllListeners();
3111         var pn = this.dom.parentNode;
3112         if(pn){
3113             pn.removeChild(this.dom);
3114         }
3115         Roo.Element.uncache(this.id);
3116     },
3117
3118     remove : function(){
3119         this.destroy();
3120     },
3121
3122     // private
3123     beginUpdate : function(){
3124         this.updating = true;
3125     },
3126
3127     // private
3128     endUpdate : function(){
3129         this.updating = false;
3130         this.sync(true);
3131     },
3132
3133     // private
3134     hideUnders : function(negOffset){
3135         if(this.shadow){
3136             this.shadow.hide();
3137         }
3138         this.hideShim();
3139     },
3140
3141     // private
3142     constrainXY : function(){
3143         if(this.constrain){
3144             var vw = Roo.lib.Dom.getViewWidth(),
3145                 vh = Roo.lib.Dom.getViewHeight();
3146             var s = Roo.get(document).getScroll();
3147
3148             var xy = this.getXY();
3149             var x = xy[0], y = xy[1];   
3150             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
3151             // only move it if it needs it
3152             var moved = false;
3153             // first validate right/bottom
3154             if((x + w) > vw+s.left){
3155                 x = vw - w - this.shadowOffset;
3156                 moved = true;
3157             }
3158             if((y + h) > vh+s.top){
3159                 y = vh - h - this.shadowOffset;
3160                 moved = true;
3161             }
3162             // then make sure top/left isn't negative
3163             if(x < s.left){
3164                 x = s.left;
3165                 moved = true;
3166             }
3167             if(y < s.top){
3168                 y = s.top;
3169                 moved = true;
3170             }
3171             if(moved){
3172                 if(this.avoidY){
3173                     var ay = this.avoidY;
3174                     if(y <= ay && (y+h) >= ay){
3175                         y = ay-h-5;   
3176                     }
3177                 }
3178                 xy = [x, y];
3179                 this.storeXY(xy);
3180                 supr.setXY.call(this, xy);
3181                 this.sync();
3182             }
3183         }
3184     },
3185
3186     isVisible : function(){
3187         return this.visible;    
3188     },
3189
3190     // private
3191     showAction : function(){
3192         this.visible = true; // track visibility to prevent getStyle calls
3193         if(this.useDisplay === true){
3194             this.setDisplayed("");
3195         }else if(this.lastXY){
3196             supr.setXY.call(this, this.lastXY);
3197         }else if(this.lastLT){
3198             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
3199         }
3200     },
3201
3202     // private
3203     hideAction : function(){
3204         this.visible = false;
3205         if(this.useDisplay === true){
3206             this.setDisplayed(false);
3207         }else{
3208             this.setLeftTop(-10000,-10000);
3209         }
3210     },
3211
3212     // overridden Element method
3213     setVisible : function(v, a, d, c, e){
3214         if(v){
3215             this.showAction();
3216         }
3217         if(a && v){
3218             var cb = function(){
3219                 this.sync(true);
3220                 if(c){
3221                     c();
3222                 }
3223             }.createDelegate(this);
3224             supr.setVisible.call(this, true, true, d, cb, e);
3225         }else{
3226             if(!v){
3227                 this.hideUnders(true);
3228             }
3229             var cb = c;
3230             if(a){
3231                 cb = function(){
3232                     this.hideAction();
3233                     if(c){
3234                         c();
3235                     }
3236                 }.createDelegate(this);
3237             }
3238             supr.setVisible.call(this, v, a, d, cb, e);
3239             if(v){
3240                 this.sync(true);
3241             }else if(!a){
3242                 this.hideAction();
3243             }
3244         }
3245     },
3246
3247     storeXY : function(xy){
3248         delete this.lastLT;
3249         this.lastXY = xy;
3250     },
3251
3252     storeLeftTop : function(left, top){
3253         delete this.lastXY;
3254         this.lastLT = [left, top];
3255     },
3256
3257     // private
3258     beforeFx : function(){
3259         this.beforeAction();
3260         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
3261     },
3262
3263     // private
3264     afterFx : function(){
3265         Roo.Layer.superclass.afterFx.apply(this, arguments);
3266         this.sync(this.isVisible());
3267     },
3268
3269     // private
3270     beforeAction : function(){
3271         if(!this.updating && this.shadow){
3272             this.shadow.hide();
3273         }
3274     },
3275
3276     // overridden Element method
3277     setLeft : function(left){
3278         this.storeLeftTop(left, this.getTop(true));
3279         supr.setLeft.apply(this, arguments);
3280         this.sync();
3281     },
3282
3283     setTop : function(top){
3284         this.storeLeftTop(this.getLeft(true), top);
3285         supr.setTop.apply(this, arguments);
3286         this.sync();
3287     },
3288
3289     setLeftTop : function(left, top){
3290         this.storeLeftTop(left, top);
3291         supr.setLeftTop.apply(this, arguments);
3292         this.sync();
3293     },
3294
3295     setXY : function(xy, a, d, c, e){
3296         this.fixDisplay();
3297         this.beforeAction();
3298         this.storeXY(xy);
3299         var cb = this.createCB(c);
3300         supr.setXY.call(this, xy, a, d, cb, e);
3301         if(!a){
3302             cb();
3303         }
3304     },
3305
3306     // private
3307     createCB : function(c){
3308         var el = this;
3309         return function(){
3310             el.constrainXY();
3311             el.sync(true);
3312             if(c){
3313                 c();
3314             }
3315         };
3316     },
3317
3318     // overridden Element method
3319     setX : function(x, a, d, c, e){
3320         this.setXY([x, this.getY()], a, d, c, e);
3321     },
3322
3323     // overridden Element method
3324     setY : function(y, a, d, c, e){
3325         this.setXY([this.getX(), y], a, d, c, e);
3326     },
3327
3328     // overridden Element method
3329     setSize : function(w, h, a, d, c, e){
3330         this.beforeAction();
3331         var cb = this.createCB(c);
3332         supr.setSize.call(this, w, h, a, d, cb, e);
3333         if(!a){
3334             cb();
3335         }
3336     },
3337
3338     // overridden Element method
3339     setWidth : function(w, a, d, c, e){
3340         this.beforeAction();
3341         var cb = this.createCB(c);
3342         supr.setWidth.call(this, w, a, d, cb, e);
3343         if(!a){
3344             cb();
3345         }
3346     },
3347
3348     // overridden Element method
3349     setHeight : function(h, a, d, c, e){
3350         this.beforeAction();
3351         var cb = this.createCB(c);
3352         supr.setHeight.call(this, h, a, d, cb, e);
3353         if(!a){
3354             cb();
3355         }
3356     },
3357
3358     // overridden Element method
3359     setBounds : function(x, y, w, h, a, d, c, e){
3360         this.beforeAction();
3361         var cb = this.createCB(c);
3362         if(!a){
3363             this.storeXY([x, y]);
3364             supr.setXY.call(this, [x, y]);
3365             supr.setSize.call(this, w, h, a, d, cb, e);
3366             cb();
3367         }else{
3368             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
3369         }
3370         return this;
3371     },
3372     
3373     /**
3374      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
3375      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
3376      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
3377      * @param {Number} zindex The new z-index to set
3378      * @return {this} The Layer
3379      */
3380     setZIndex : function(zindex){
3381         this.zindex = zindex;
3382         this.setStyle("z-index", zindex + 2);
3383         if(this.shadow){
3384             this.shadow.setZIndex(zindex + 1);
3385         }
3386         if(this.shim){
3387             this.shim.setStyle("z-index", zindex);
3388         }
3389     }
3390 });
3391 })();/*
3392  * Based on:
3393  * Ext JS Library 1.1.1
3394  * Copyright(c) 2006-2007, Ext JS, LLC.
3395  *
3396  * Originally Released Under LGPL - original licence link has changed is not relivant.
3397  *
3398  * Fork - LGPL
3399  * <script type="text/javascript">
3400  */
3401
3402
3403 /**
3404  * @class Roo.Shadow
3405  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
3406  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
3407  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
3408  * @constructor
3409  * Create a new Shadow
3410  * @param {Object} config The config object
3411  */
3412 Roo.Shadow = function(config){
3413     Roo.apply(this, config);
3414     if(typeof this.mode != "string"){
3415         this.mode = this.defaultMode;
3416     }
3417     var o = this.offset, a = {h: 0};
3418     var rad = Math.floor(this.offset/2);
3419     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
3420         case "drop":
3421             a.w = 0;
3422             a.l = a.t = o;
3423             a.t -= 1;
3424             if(Roo.isIE){
3425                 a.l -= this.offset + rad;
3426                 a.t -= this.offset + rad;
3427                 a.w -= rad;
3428                 a.h -= rad;
3429                 a.t += 1;
3430             }
3431         break;
3432         case "sides":
3433             a.w = (o*2);
3434             a.l = -o;
3435             a.t = o-1;
3436             if(Roo.isIE){
3437                 a.l -= (this.offset - rad);
3438                 a.t -= this.offset + rad;
3439                 a.l += 1;
3440                 a.w -= (this.offset - rad)*2;
3441                 a.w -= rad + 1;
3442                 a.h -= 1;
3443             }
3444         break;
3445         case "frame":
3446             a.w = a.h = (o*2);
3447             a.l = a.t = -o;
3448             a.t += 1;
3449             a.h -= 2;
3450             if(Roo.isIE){
3451                 a.l -= (this.offset - rad);
3452                 a.t -= (this.offset - rad);
3453                 a.l += 1;
3454                 a.w -= (this.offset + rad + 1);
3455                 a.h -= (this.offset + rad);
3456                 a.h += 1;
3457             }
3458         break;
3459     };
3460
3461     this.adjusts = a;
3462 };
3463
3464 Roo.Shadow.prototype = {
3465     /**
3466      * @cfg {String} mode
3467      * The shadow display mode.  Supports the following options:<br />
3468      * sides: Shadow displays on both sides and bottom only<br />
3469      * frame: Shadow displays equally on all four sides<br />
3470      * drop: Traditional bottom-right drop shadow (default)
3471      */
3472     /**
3473      * @cfg {String} offset
3474      * The number of pixels to offset the shadow from the element (defaults to 4)
3475      */
3476     offset: 4,
3477
3478     // private
3479     defaultMode: "drop",
3480
3481     /**
3482      * Displays the shadow under the target element
3483      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
3484      */
3485     show : function(target){
3486         target = Roo.get(target);
3487         if(!this.el){
3488             this.el = Roo.Shadow.Pool.pull();
3489             if(this.el.dom.nextSibling != target.dom){
3490                 this.el.insertBefore(target);
3491             }
3492         }
3493         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
3494         if(Roo.isIE){
3495             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
3496         }
3497         this.realign(
3498             target.getLeft(true),
3499             target.getTop(true),
3500             target.getWidth(),
3501             target.getHeight()
3502         );
3503         this.el.dom.style.display = "block";
3504     },
3505
3506     /**
3507      * Returns true if the shadow is visible, else false
3508      */
3509     isVisible : function(){
3510         return this.el ? true : false;  
3511     },
3512
3513     /**
3514      * Direct alignment when values are already available. Show must be called at least once before
3515      * calling this method to ensure it is initialized.
3516      * @param {Number} left The target element left position
3517      * @param {Number} top The target element top position
3518      * @param {Number} width The target element width
3519      * @param {Number} height The target element height
3520      */
3521     realign : function(l, t, w, h){
3522         if(!this.el){
3523             return;
3524         }
3525         var a = this.adjusts, d = this.el.dom, s = d.style;
3526         var iea = 0;
3527         s.left = (l+a.l)+"px";
3528         s.top = (t+a.t)+"px";
3529         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
3530  
3531         if(s.width != sws || s.height != shs){
3532             s.width = sws;
3533             s.height = shs;
3534             if(!Roo.isIE){
3535                 var cn = d.childNodes;
3536                 var sww = Math.max(0, (sw-12))+"px";
3537                 cn[0].childNodes[1].style.width = sww;
3538                 cn[1].childNodes[1].style.width = sww;
3539                 cn[2].childNodes[1].style.width = sww;
3540                 cn[1].style.height = Math.max(0, (sh-12))+"px";
3541             }
3542         }
3543     },
3544
3545     /**
3546      * Hides this shadow
3547      */
3548     hide : function(){
3549         if(this.el){
3550             this.el.dom.style.display = "none";
3551             Roo.Shadow.Pool.push(this.el);
3552             delete this.el;
3553         }
3554     },
3555
3556     /**
3557      * Adjust the z-index of this shadow
3558      * @param {Number} zindex The new z-index
3559      */
3560     setZIndex : function(z){
3561         this.zIndex = z;
3562         if(this.el){
3563             this.el.setStyle("z-index", z);
3564         }
3565     }
3566 };
3567
3568 // Private utility class that manages the internal Shadow cache
3569 Roo.Shadow.Pool = function(){
3570     var p = [];
3571     var markup = Roo.isIE ?
3572                  '<div class="x-ie-shadow"></div>' :
3573                  '<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>';
3574     return {
3575         pull : function(){
3576             var sh = p.shift();
3577             if(!sh){
3578                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
3579                 sh.autoBoxAdjust = false;
3580             }
3581             return sh;
3582         },
3583
3584         push : function(sh){
3585             p.push(sh);
3586         }
3587     };
3588 }();/*
3589  * Based on:
3590  * Ext JS Library 1.1.1
3591  * Copyright(c) 2006-2007, Ext JS, LLC.
3592  *
3593  * Originally Released Under LGPL - original licence link has changed is not relivant.
3594  *
3595  * Fork - LGPL
3596  * <script type="text/javascript">
3597  */
3598
3599
3600 /**
3601  * @class Roo.SplitBar
3602  * @extends Roo.util.Observable
3603  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
3604  * <br><br>
3605  * Usage:
3606  * <pre><code>
3607 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
3608                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
3609 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
3610 split.minSize = 100;
3611 split.maxSize = 600;
3612 split.animate = true;
3613 split.on('moved', splitterMoved);
3614 </code></pre>
3615  * @constructor
3616  * Create a new SplitBar
3617  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
3618  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
3619  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3620  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
3621                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
3622                         position of the SplitBar).
3623  */
3624 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
3625     
3626     /** @private */
3627     this.el = Roo.get(dragElement, true);
3628     this.el.dom.unselectable = "on";
3629     /** @private */
3630     this.resizingEl = Roo.get(resizingElement, true);
3631
3632     /**
3633      * @private
3634      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3635      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
3636      * @type Number
3637      */
3638     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
3639     
3640     /**
3641      * The minimum size of the resizing element. (Defaults to 0)
3642      * @type Number
3643      */
3644     this.minSize = 0;
3645     
3646     /**
3647      * The maximum size of the resizing element. (Defaults to 2000)
3648      * @type Number
3649      */
3650     this.maxSize = 2000;
3651     
3652     /**
3653      * Whether to animate the transition to the new size
3654      * @type Boolean
3655      */
3656     this.animate = false;
3657     
3658     /**
3659      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
3660      * @type Boolean
3661      */
3662     this.useShim = false;
3663     
3664     /** @private */
3665     this.shim = null;
3666     
3667     if(!existingProxy){
3668         /** @private */
3669         this.proxy = Roo.SplitBar.createProxy(this.orientation);
3670     }else{
3671         this.proxy = Roo.get(existingProxy).dom;
3672     }
3673     /** @private */
3674     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
3675     
3676     /** @private */
3677     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
3678     
3679     /** @private */
3680     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
3681     
3682     /** @private */
3683     this.dragSpecs = {};
3684     
3685     /**
3686      * @private The adapter to use to positon and resize elements
3687      */
3688     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
3689     this.adapter.init(this);
3690     
3691     if(this.orientation == Roo.SplitBar.HORIZONTAL){
3692         /** @private */
3693         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
3694         this.el.addClass("x-splitbar-h");
3695     }else{
3696         /** @private */
3697         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
3698         this.el.addClass("x-splitbar-v");
3699     }
3700     
3701     this.addEvents({
3702         /**
3703          * @event resize
3704          * Fires when the splitter is moved (alias for {@link #event-moved})
3705          * @param {Roo.SplitBar} this
3706          * @param {Number} newSize the new width or height
3707          */
3708         "resize" : true,
3709         /**
3710          * @event moved
3711          * Fires when the splitter is moved
3712          * @param {Roo.SplitBar} this
3713          * @param {Number} newSize the new width or height
3714          */
3715         "moved" : true,
3716         /**
3717          * @event beforeresize
3718          * Fires before the splitter is dragged
3719          * @param {Roo.SplitBar} this
3720          */
3721         "beforeresize" : true,
3722
3723         "beforeapply" : true
3724     });
3725
3726     Roo.util.Observable.call(this);
3727 };
3728
3729 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
3730     onStartProxyDrag : function(x, y){
3731         this.fireEvent("beforeresize", this);
3732         if(!this.overlay){
3733             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
3734             o.unselectable();
3735             o.enableDisplayMode("block");
3736             // all splitbars share the same overlay
3737             Roo.SplitBar.prototype.overlay = o;
3738         }
3739         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
3740         this.overlay.show();
3741         Roo.get(this.proxy).setDisplayed("block");
3742         var size = this.adapter.getElementSize(this);
3743         this.activeMinSize = this.getMinimumSize();;
3744         this.activeMaxSize = this.getMaximumSize();;
3745         var c1 = size - this.activeMinSize;
3746         var c2 = Math.max(this.activeMaxSize - size, 0);
3747         if(this.orientation == Roo.SplitBar.HORIZONTAL){
3748             this.dd.resetConstraints();
3749             this.dd.setXConstraint(
3750                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
3751                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
3752             );
3753             this.dd.setYConstraint(0, 0);
3754         }else{
3755             this.dd.resetConstraints();
3756             this.dd.setXConstraint(0, 0);
3757             this.dd.setYConstraint(
3758                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
3759                 this.placement == Roo.SplitBar.TOP ? c2 : c1
3760             );
3761          }
3762         this.dragSpecs.startSize = size;
3763         this.dragSpecs.startPoint = [x, y];
3764         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
3765     },
3766     
3767     /** 
3768      * @private Called after the drag operation by the DDProxy
3769      */
3770     onEndProxyDrag : function(e){
3771         Roo.get(this.proxy).setDisplayed(false);
3772         var endPoint = Roo.lib.Event.getXY(e);
3773         if(this.overlay){
3774             this.overlay.hide();
3775         }
3776         var newSize;
3777         if(this.orientation == Roo.SplitBar.HORIZONTAL){
3778             newSize = this.dragSpecs.startSize + 
3779                 (this.placement == Roo.SplitBar.LEFT ?
3780                     endPoint[0] - this.dragSpecs.startPoint[0] :
3781                     this.dragSpecs.startPoint[0] - endPoint[0]
3782                 );
3783         }else{
3784             newSize = this.dragSpecs.startSize + 
3785                 (this.placement == Roo.SplitBar.TOP ?
3786                     endPoint[1] - this.dragSpecs.startPoint[1] :
3787                     this.dragSpecs.startPoint[1] - endPoint[1]
3788                 );
3789         }
3790         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
3791         if(newSize != this.dragSpecs.startSize){
3792             if(this.fireEvent('beforeapply', this, newSize) !== false){
3793                 this.adapter.setElementSize(this, newSize);
3794                 this.fireEvent("moved", this, newSize);
3795                 this.fireEvent("resize", this, newSize);
3796             }
3797         }
3798     },
3799     
3800     /**
3801      * Get the adapter this SplitBar uses
3802      * @return The adapter object
3803      */
3804     getAdapter : function(){
3805         return this.adapter;
3806     },
3807     
3808     /**
3809      * Set the adapter this SplitBar uses
3810      * @param {Object} adapter A SplitBar adapter object
3811      */
3812     setAdapter : function(adapter){
3813         this.adapter = adapter;
3814         this.adapter.init(this);
3815     },
3816     
3817     /**
3818      * Gets the minimum size for the resizing element
3819      * @return {Number} The minimum size
3820      */
3821     getMinimumSize : function(){
3822         return this.minSize;
3823     },
3824     
3825     /**
3826      * Sets the minimum size for the resizing element
3827      * @param {Number} minSize The minimum size
3828      */
3829     setMinimumSize : function(minSize){
3830         this.minSize = minSize;
3831     },
3832     
3833     /**
3834      * Gets the maximum size for the resizing element
3835      * @return {Number} The maximum size
3836      */
3837     getMaximumSize : function(){
3838         return this.maxSize;
3839     },
3840     
3841     /**
3842      * Sets the maximum size for the resizing element
3843      * @param {Number} maxSize The maximum size
3844      */
3845     setMaximumSize : function(maxSize){
3846         this.maxSize = maxSize;
3847     },
3848     
3849     /**
3850      * Sets the initialize size for the resizing element
3851      * @param {Number} size The initial size
3852      */
3853     setCurrentSize : function(size){
3854         var oldAnimate = this.animate;
3855         this.animate = false;
3856         this.adapter.setElementSize(this, size);
3857         this.animate = oldAnimate;
3858     },
3859     
3860     /**
3861      * Destroy this splitbar. 
3862      * @param {Boolean} removeEl True to remove the element
3863      */
3864     destroy : function(removeEl){
3865         if(this.shim){
3866             this.shim.remove();
3867         }
3868         this.dd.unreg();
3869         this.proxy.parentNode.removeChild(this.proxy);
3870         if(removeEl){
3871             this.el.remove();
3872         }
3873     }
3874 });
3875
3876 /**
3877  * @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.
3878  */
3879 Roo.SplitBar.createProxy = function(dir){
3880     var proxy = new Roo.Element(document.createElement("div"));
3881     proxy.unselectable();
3882     var cls = 'x-splitbar-proxy';
3883     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
3884     document.body.appendChild(proxy.dom);
3885     return proxy.dom;
3886 };
3887
3888 /** 
3889  * @class Roo.SplitBar.BasicLayoutAdapter
3890  * Default Adapter. It assumes the splitter and resizing element are not positioned
3891  * elements and only gets/sets the width of the element. Generally used for table based layouts.
3892  */
3893 Roo.SplitBar.BasicLayoutAdapter = function(){
3894 };
3895
3896 Roo.SplitBar.BasicLayoutAdapter.prototype = {
3897     // do nothing for now
3898     init : function(s){
3899     
3900     },
3901     /**
3902      * Called before drag operations to get the current size of the resizing element. 
3903      * @param {Roo.SplitBar} s The SplitBar using this adapter
3904      */
3905      getElementSize : function(s){
3906         if(s.orientation == Roo.SplitBar.HORIZONTAL){
3907             return s.resizingEl.getWidth();
3908         }else{
3909             return s.resizingEl.getHeight();
3910         }
3911     },
3912     
3913     /**
3914      * Called after drag operations to set the size of the resizing element.
3915      * @param {Roo.SplitBar} s The SplitBar using this adapter
3916      * @param {Number} newSize The new size to set
3917      * @param {Function} onComplete A function to be invoked when resizing is complete
3918      */
3919     setElementSize : function(s, newSize, onComplete){
3920         if(s.orientation == Roo.SplitBar.HORIZONTAL){
3921             if(!s.animate){
3922                 s.resizingEl.setWidth(newSize);
3923                 if(onComplete){
3924                     onComplete(s, newSize);
3925                 }
3926             }else{
3927                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
3928             }
3929         }else{
3930             
3931             if(!s.animate){
3932                 s.resizingEl.setHeight(newSize);
3933                 if(onComplete){
3934                     onComplete(s, newSize);
3935                 }
3936             }else{
3937                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
3938             }
3939         }
3940     }
3941 };
3942
3943 /** 
3944  *@class Roo.SplitBar.AbsoluteLayoutAdapter
3945  * @extends Roo.SplitBar.BasicLayoutAdapter
3946  * Adapter that  moves the splitter element to align with the resized sizing element. 
3947  * Used with an absolute positioned SplitBar.
3948  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
3949  * document.body, make sure you assign an id to the body element.
3950  */
3951 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
3952     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
3953     this.container = Roo.get(container);
3954 };
3955
3956 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
3957     init : function(s){
3958         this.basic.init(s);
3959     },
3960     
3961     getElementSize : function(s){
3962         return this.basic.getElementSize(s);
3963     },
3964     
3965     setElementSize : function(s, newSize, onComplete){
3966         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
3967     },
3968     
3969     moveSplitter : function(s){
3970         var yes = Roo.SplitBar;
3971         switch(s.placement){
3972             case yes.LEFT:
3973                 s.el.setX(s.resizingEl.getRight());
3974                 break;
3975             case yes.RIGHT:
3976                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
3977                 break;
3978             case yes.TOP:
3979                 s.el.setY(s.resizingEl.getBottom());
3980                 break;
3981             case yes.BOTTOM:
3982                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
3983                 break;
3984         }
3985     }
3986 };
3987
3988 /**
3989  * Orientation constant - Create a vertical SplitBar
3990  * @static
3991  * @type Number
3992  */
3993 Roo.SplitBar.VERTICAL = 1;
3994
3995 /**
3996  * Orientation constant - Create a horizontal SplitBar
3997  * @static
3998  * @type Number
3999  */
4000 Roo.SplitBar.HORIZONTAL = 2;
4001
4002 /**
4003  * Placement constant - The resizing element is to the left of the splitter element
4004  * @static
4005  * @type Number
4006  */
4007 Roo.SplitBar.LEFT = 1;
4008
4009 /**
4010  * Placement constant - The resizing element is to the right of the splitter element
4011  * @static
4012  * @type Number
4013  */
4014 Roo.SplitBar.RIGHT = 2;
4015
4016 /**
4017  * Placement constant - The resizing element is positioned above the splitter element
4018  * @static
4019  * @type Number
4020  */
4021 Roo.SplitBar.TOP = 3;
4022
4023 /**
4024  * Placement constant - The resizing element is positioned under splitter element
4025  * @static
4026  * @type Number
4027  */
4028 Roo.SplitBar.BOTTOM = 4;
4029 /*
4030  * Based on:
4031  * Ext JS Library 1.1.1
4032  * Copyright(c) 2006-2007, Ext JS, LLC.
4033  *
4034  * Originally Released Under LGPL - original licence link has changed is not relivant.
4035  *
4036  * Fork - LGPL
4037  * <script type="text/javascript">
4038  */
4039
4040 /**
4041  * @class Roo.View
4042  * @extends Roo.util.Observable
4043  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
4044  * This class also supports single and multi selection modes. <br>
4045  * Create a data model bound view:
4046  <pre><code>
4047  var store = new Roo.data.Store(...);
4048
4049  var view = new Roo.View({
4050     el : "my-element",
4051     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
4052  
4053     singleSelect: true,
4054     selectedClass: "ydataview-selected",
4055     store: store
4056  });
4057
4058  // listen for node click?
4059  view.on("click", function(vw, index, node, e){
4060  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
4061  });
4062
4063  // load XML data
4064  dataModel.load("foobar.xml");
4065  </code></pre>
4066  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
4067  * <br><br>
4068  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
4069  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
4070  * 
4071  * Note: old style constructor is still suported (container, template, config)
4072  * 
4073  * @constructor
4074  * Create a new View
4075  * @param {Object} config The config object
4076  * 
4077  */
4078 Roo.View = function(config, depreciated_tpl, depreciated_config){
4079     
4080     this.parent = false;
4081     
4082     if (typeof(depreciated_tpl) == 'undefined') {
4083         // new way.. - universal constructor.
4084         Roo.apply(this, config);
4085         this.el  = Roo.get(this.el);
4086     } else {
4087         // old format..
4088         this.el  = Roo.get(config);
4089         this.tpl = depreciated_tpl;
4090         Roo.apply(this, depreciated_config);
4091     }
4092     this.wrapEl  = this.el.wrap().wrap();
4093     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
4094     
4095     
4096     if(typeof(this.tpl) == "string"){
4097         this.tpl = new Roo.Template(this.tpl);
4098     } else {
4099         // support xtype ctors..
4100         this.tpl = new Roo.factory(this.tpl, Roo);
4101     }
4102     
4103     
4104     this.tpl.compile();
4105     
4106     /** @private */
4107     this.addEvents({
4108         /**
4109          * @event beforeclick
4110          * Fires before a click is processed. Returns false to cancel the default action.
4111          * @param {Roo.View} this
4112          * @param {Number} index The index of the target node
4113          * @param {HTMLElement} node The target node
4114          * @param {Roo.EventObject} e The raw event object
4115          */
4116             "beforeclick" : true,
4117         /**
4118          * @event click
4119          * Fires when a template node is clicked.
4120          * @param {Roo.View} this
4121          * @param {Number} index The index of the target node
4122          * @param {HTMLElement} node The target node
4123          * @param {Roo.EventObject} e The raw event object
4124          */
4125             "click" : true,
4126         /**
4127          * @event dblclick
4128          * Fires when a template node is double clicked.
4129          * @param {Roo.View} this
4130          * @param {Number} index The index of the target node
4131          * @param {HTMLElement} node The target node
4132          * @param {Roo.EventObject} e The raw event object
4133          */
4134             "dblclick" : true,
4135         /**
4136          * @event contextmenu
4137          * Fires when a template node is right clicked.
4138          * @param {Roo.View} this
4139          * @param {Number} index The index of the target node
4140          * @param {HTMLElement} node The target node
4141          * @param {Roo.EventObject} e The raw event object
4142          */
4143             "contextmenu" : true,
4144         /**
4145          * @event selectionchange
4146          * Fires when the selected nodes change.
4147          * @param {Roo.View} this
4148          * @param {Array} selections Array of the selected nodes
4149          */
4150             "selectionchange" : true,
4151     
4152         /**
4153          * @event beforeselect
4154          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
4155          * @param {Roo.View} this
4156          * @param {HTMLElement} node The node to be selected
4157          * @param {Array} selections Array of currently selected nodes
4158          */
4159             "beforeselect" : true,
4160         /**
4161          * @event preparedata
4162          * Fires on every row to render, to allow you to change the data.
4163          * @param {Roo.View} this
4164          * @param {Object} data to be rendered (change this)
4165          */
4166           "preparedata" : true
4167           
4168           
4169         });
4170
4171
4172
4173     this.el.on({
4174         "click": this.onClick,
4175         "dblclick": this.onDblClick,
4176         "contextmenu": this.onContextMenu,
4177         scope:this
4178     });
4179
4180     this.selections = [];
4181     this.nodes = [];
4182     this.cmp = new Roo.CompositeElementLite([]);
4183     if(this.store){
4184         this.store = Roo.factory(this.store, Roo.data);
4185         this.setStore(this.store, true);
4186     }
4187     
4188     if ( this.footer && this.footer.xtype) {
4189            
4190          var fctr = this.wrapEl.appendChild(document.createElement("div"));
4191         
4192         this.footer.dataSource = this.store;
4193         this.footer.container = fctr;
4194         this.footer = Roo.factory(this.footer, Roo);
4195         fctr.insertFirst(this.el);
4196         
4197         // this is a bit insane - as the paging toolbar seems to detach the el..
4198 //        dom.parentNode.parentNode.parentNode
4199          // they get detached?
4200     }
4201     
4202     
4203     Roo.View.superclass.constructor.call(this);
4204     
4205     
4206 };
4207
4208 Roo.extend(Roo.View, Roo.util.Observable, {
4209     
4210      /**
4211      * @cfg {Roo.data.Store} store Data store to load data from.
4212      */
4213     store : false,
4214     
4215     /**
4216      * @cfg {String|Roo.Element} el The container element.
4217      */
4218     el : '',
4219     
4220     /**
4221      * @cfg {String|Roo.Template} tpl The template used by this View 
4222      */
4223     tpl : false,
4224     /**
4225      * @cfg {String} dataName the named area of the template to use as the data area
4226      *                          Works with domtemplates roo-name="name"
4227      */
4228     dataName: false,
4229     /**
4230      * @cfg {String} selectedClass The css class to add to selected nodes
4231      */
4232     selectedClass : "x-view-selected",
4233      /**
4234      * @cfg {String} emptyText The empty text to show when nothing is loaded.
4235      */
4236     emptyText : "",
4237     
4238     /**
4239      * @cfg {String} text to display on mask (default Loading)
4240      */
4241     mask : false,
4242     /**
4243      * @cfg {Boolean} multiSelect Allow multiple selection
4244      */
4245     multiSelect : false,
4246     /**
4247      * @cfg {Boolean} singleSelect Allow single selection
4248      */
4249     singleSelect:  false,
4250     
4251     /**
4252      * @cfg {Boolean} toggleSelect - selecting 
4253      */
4254     toggleSelect : false,
4255     
4256     /**
4257      * @cfg {Boolean} tickable - selecting 
4258      */
4259     tickable : false,
4260     
4261     /**
4262      * Returns the element this view is bound to.
4263      * @return {Roo.Element}
4264      */
4265     getEl : function(){
4266         return this.wrapEl;
4267     },
4268     
4269     
4270
4271     /**
4272      * Refreshes the view. - called by datachanged on the store. - do not call directly.
4273      */
4274     refresh : function(){
4275         //Roo.log('refresh');
4276         var t = this.tpl;
4277         
4278         // if we are using something like 'domtemplate', then
4279         // the what gets used is:
4280         // t.applySubtemplate(NAME, data, wrapping data..)
4281         // the outer template then get' applied with
4282         //     the store 'extra data'
4283         // and the body get's added to the
4284         //      roo-name="data" node?
4285         //      <span class='roo-tpl-{name}'></span> ?????
4286         
4287         
4288         
4289         this.clearSelections();
4290         this.el.update("");
4291         var html = [];
4292         var records = this.store.getRange();
4293         if(records.length < 1) {
4294             
4295             // is this valid??  = should it render a template??
4296             
4297             this.el.update(this.emptyText);
4298             return;
4299         }
4300         var el = this.el;
4301         if (this.dataName) {
4302             this.el.update(t.apply(this.store.meta)); //????
4303             el = this.el.child('.roo-tpl-' + this.dataName);
4304         }
4305         
4306         for(var i = 0, len = records.length; i < len; i++){
4307             var data = this.prepareData(records[i].data, i, records[i]);
4308             this.fireEvent("preparedata", this, data, i, records[i]);
4309             
4310             var d = Roo.apply({}, data);
4311             
4312             if(this.tickable){
4313                 Roo.apply(d, {'roo-id' : Roo.id()});
4314                 
4315                 var _this = this;
4316             
4317                 Roo.each(this.parent.item, function(item){
4318                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
4319                         return;
4320                     }
4321                     Roo.apply(d, {'roo-data-checked' : 'checked'});
4322                 });
4323             }
4324             
4325             html[html.length] = Roo.util.Format.trim(
4326                 this.dataName ?
4327                     t.applySubtemplate(this.dataName, d, this.store.meta) :
4328                     t.apply(d)
4329             );
4330         }
4331         
4332         
4333         
4334         el.update(html.join(""));
4335         this.nodes = el.dom.childNodes;
4336         this.updateIndexes(0);
4337     },
4338     
4339
4340     /**
4341      * Function to override to reformat the data that is sent to
4342      * the template for each node.
4343      * DEPRICATED - use the preparedata event handler.
4344      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
4345      * a JSON object for an UpdateManager bound view).
4346      */
4347     prepareData : function(data, index, record)
4348     {
4349         this.fireEvent("preparedata", this, data, index, record);
4350         return data;
4351     },
4352
4353     onUpdate : function(ds, record){
4354         // Roo.log('on update');   
4355         this.clearSelections();
4356         var index = this.store.indexOf(record);
4357         var n = this.nodes[index];
4358         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
4359         n.parentNode.removeChild(n);
4360         this.updateIndexes(index, index);
4361     },
4362
4363     
4364     
4365 // --------- FIXME     
4366     onAdd : function(ds, records, index)
4367     {
4368         //Roo.log(['on Add', ds, records, index] );        
4369         this.clearSelections();
4370         if(this.nodes.length == 0){
4371             this.refresh();
4372             return;
4373         }
4374         var n = this.nodes[index];
4375         for(var i = 0, len = records.length; i < len; i++){
4376             var d = this.prepareData(records[i].data, i, records[i]);
4377             if(n){
4378                 this.tpl.insertBefore(n, d);
4379             }else{
4380                 
4381                 this.tpl.append(this.el, d);
4382             }
4383         }
4384         this.updateIndexes(index);
4385     },
4386
4387     onRemove : function(ds, record, index){
4388        // Roo.log('onRemove');
4389         this.clearSelections();
4390         var el = this.dataName  ?
4391             this.el.child('.roo-tpl-' + this.dataName) :
4392             this.el; 
4393         
4394         el.dom.removeChild(this.nodes[index]);
4395         this.updateIndexes(index);
4396     },
4397
4398     /**
4399      * Refresh an individual node.
4400      * @param {Number} index
4401      */
4402     refreshNode : function(index){
4403         this.onUpdate(this.store, this.store.getAt(index));
4404     },
4405
4406     updateIndexes : function(startIndex, endIndex){
4407         var ns = this.nodes;
4408         startIndex = startIndex || 0;
4409         endIndex = endIndex || ns.length - 1;
4410         for(var i = startIndex; i <= endIndex; i++){
4411             ns[i].nodeIndex = i;
4412         }
4413     },
4414
4415     /**
4416      * Changes the data store this view uses and refresh the view.
4417      * @param {Store} store
4418      */
4419     setStore : function(store, initial){
4420         if(!initial && this.store){
4421             this.store.un("datachanged", this.refresh);
4422             this.store.un("add", this.onAdd);
4423             this.store.un("remove", this.onRemove);
4424             this.store.un("update", this.onUpdate);
4425             this.store.un("clear", this.refresh);
4426             this.store.un("beforeload", this.onBeforeLoad);
4427             this.store.un("load", this.onLoad);
4428             this.store.un("loadexception", this.onLoad);
4429         }
4430         if(store){
4431           
4432             store.on("datachanged", this.refresh, this);
4433             store.on("add", this.onAdd, this);
4434             store.on("remove", this.onRemove, this);
4435             store.on("update", this.onUpdate, this);
4436             store.on("clear", this.refresh, this);
4437             store.on("beforeload", this.onBeforeLoad, this);
4438             store.on("load", this.onLoad, this);
4439             store.on("loadexception", this.onLoad, this);
4440         }
4441         
4442         if(store){
4443             this.refresh();
4444         }
4445     },
4446     /**
4447      * onbeforeLoad - masks the loading area.
4448      *
4449      */
4450     onBeforeLoad : function(store,opts)
4451     {
4452          //Roo.log('onBeforeLoad');   
4453         if (!opts.add) {
4454             this.el.update("");
4455         }
4456         this.el.mask(this.mask ? this.mask : "Loading" ); 
4457     },
4458     onLoad : function ()
4459     {
4460         this.el.unmask();
4461     },
4462     
4463
4464     /**
4465      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
4466      * @param {HTMLElement} node
4467      * @return {HTMLElement} The template node
4468      */
4469     findItemFromChild : function(node){
4470         var el = this.dataName  ?
4471             this.el.child('.roo-tpl-' + this.dataName,true) :
4472             this.el.dom; 
4473         
4474         if(!node || node.parentNode == el){
4475                     return node;
4476             }
4477             var p = node.parentNode;
4478             while(p && p != el){
4479             if(p.parentNode == el){
4480                 return p;
4481             }
4482             p = p.parentNode;
4483         }
4484             return null;
4485     },
4486
4487     /** @ignore */
4488     onClick : function(e){
4489         var item = this.findItemFromChild(e.getTarget());
4490         if(item){
4491             var index = this.indexOf(item);
4492             if(this.onItemClick(item, index, e) !== false){
4493                 this.fireEvent("click", this, index, item, e);
4494             }
4495         }else{
4496             this.clearSelections();
4497         }
4498     },
4499
4500     /** @ignore */
4501     onContextMenu : function(e){
4502         var item = this.findItemFromChild(e.getTarget());
4503         if(item){
4504             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
4505         }
4506     },
4507
4508     /** @ignore */
4509     onDblClick : function(e){
4510         var item = this.findItemFromChild(e.getTarget());
4511         if(item){
4512             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
4513         }
4514     },
4515
4516     onItemClick : function(item, index, e)
4517     {
4518         if(this.fireEvent("beforeclick", this, index, item, e) === false){
4519             return false;
4520         }
4521         if (this.toggleSelect) {
4522             var m = this.isSelected(item) ? 'unselect' : 'select';
4523             //Roo.log(m);
4524             var _t = this;
4525             _t[m](item, true, false);
4526             return true;
4527         }
4528         if(this.multiSelect || this.singleSelect){
4529             if(this.multiSelect && e.shiftKey && this.lastSelection){
4530                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
4531             }else{
4532                 this.select(item, this.multiSelect && e.ctrlKey);
4533                 this.lastSelection = item;
4534             }
4535             
4536             if(!this.tickable){
4537                 e.preventDefault();
4538             }
4539             
4540         }
4541         return true;
4542     },
4543
4544     /**
4545      * Get the number of selected nodes.
4546      * @return {Number}
4547      */
4548     getSelectionCount : function(){
4549         return this.selections.length;
4550     },
4551
4552     /**
4553      * Get the currently selected nodes.
4554      * @return {Array} An array of HTMLElements
4555      */
4556     getSelectedNodes : function(){
4557         return this.selections;
4558     },
4559
4560     /**
4561      * Get the indexes of the selected nodes.
4562      * @return {Array}
4563      */
4564     getSelectedIndexes : function(){
4565         var indexes = [], s = this.selections;
4566         for(var i = 0, len = s.length; i < len; i++){
4567             indexes.push(s[i].nodeIndex);
4568         }
4569         return indexes;
4570     },
4571
4572     /**
4573      * Clear all selections
4574      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
4575      */
4576     clearSelections : function(suppressEvent){
4577         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
4578             this.cmp.elements = this.selections;
4579             this.cmp.removeClass(this.selectedClass);
4580             this.selections = [];
4581             if(!suppressEvent){
4582                 this.fireEvent("selectionchange", this, this.selections);
4583             }
4584         }
4585     },
4586
4587     /**
4588      * Returns true if the passed node is selected
4589      * @param {HTMLElement/Number} node The node or node index
4590      * @return {Boolean}
4591      */
4592     isSelected : function(node){
4593         var s = this.selections;
4594         if(s.length < 1){
4595             return false;
4596         }
4597         node = this.getNode(node);
4598         return s.indexOf(node) !== -1;
4599     },
4600
4601     /**
4602      * Selects nodes.
4603      * @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
4604      * @param {Boolean} keepExisting (optional) true to keep existing selections
4605      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4606      */
4607     select : function(nodeInfo, keepExisting, suppressEvent){
4608         if(nodeInfo instanceof Array){
4609             if(!keepExisting){
4610                 this.clearSelections(true);
4611             }
4612             for(var i = 0, len = nodeInfo.length; i < len; i++){
4613                 this.select(nodeInfo[i], true, true);
4614             }
4615             return;
4616         } 
4617         var node = this.getNode(nodeInfo);
4618         if(!node || this.isSelected(node)){
4619             return; // already selected.
4620         }
4621         if(!keepExisting){
4622             this.clearSelections(true);
4623         }
4624         
4625         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
4626             Roo.fly(node).addClass(this.selectedClass);
4627             this.selections.push(node);
4628             if(!suppressEvent){
4629                 this.fireEvent("selectionchange", this, this.selections);
4630             }
4631         }
4632         
4633         
4634     },
4635       /**
4636      * Unselects nodes.
4637      * @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
4638      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
4639      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4640      */
4641     unselect : function(nodeInfo, keepExisting, suppressEvent)
4642     {
4643         if(nodeInfo instanceof Array){
4644             Roo.each(this.selections, function(s) {
4645                 this.unselect(s, nodeInfo);
4646             }, this);
4647             return;
4648         }
4649         var node = this.getNode(nodeInfo);
4650         if(!node || !this.isSelected(node)){
4651             //Roo.log("not selected");
4652             return; // not selected.
4653         }
4654         // fireevent???
4655         var ns = [];
4656         Roo.each(this.selections, function(s) {
4657             if (s == node ) {
4658                 Roo.fly(node).removeClass(this.selectedClass);
4659
4660                 return;
4661             }
4662             ns.push(s);
4663         },this);
4664         
4665         this.selections= ns;
4666         this.fireEvent("selectionchange", this, this.selections);
4667     },
4668
4669     /**
4670      * Gets a template node.
4671      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4672      * @return {HTMLElement} The node or null if it wasn't found
4673      */
4674     getNode : function(nodeInfo){
4675         if(typeof nodeInfo == "string"){
4676             return document.getElementById(nodeInfo);
4677         }else if(typeof nodeInfo == "number"){
4678             return this.nodes[nodeInfo];
4679         }
4680         return nodeInfo;
4681     },
4682
4683     /**
4684      * Gets a range template nodes.
4685      * @param {Number} startIndex
4686      * @param {Number} endIndex
4687      * @return {Array} An array of nodes
4688      */
4689     getNodes : function(start, end){
4690         var ns = this.nodes;
4691         start = start || 0;
4692         end = typeof end == "undefined" ? ns.length - 1 : end;
4693         var nodes = [];
4694         if(start <= end){
4695             for(var i = start; i <= end; i++){
4696                 nodes.push(ns[i]);
4697             }
4698         } else{
4699             for(var i = start; i >= end; i--){
4700                 nodes.push(ns[i]);
4701             }
4702         }
4703         return nodes;
4704     },
4705
4706     /**
4707      * Finds the index of the passed node
4708      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4709      * @return {Number} The index of the node or -1
4710      */
4711     indexOf : function(node){
4712         node = this.getNode(node);
4713         if(typeof node.nodeIndex == "number"){
4714             return node.nodeIndex;
4715         }
4716         var ns = this.nodes;
4717         for(var i = 0, len = ns.length; i < len; i++){
4718             if(ns[i] == node){
4719                 return i;
4720             }
4721         }
4722         return -1;
4723     }
4724 });
4725 /*
4726  * Based on:
4727  * Ext JS Library 1.1.1
4728  * Copyright(c) 2006-2007, Ext JS, LLC.
4729  *
4730  * Originally Released Under LGPL - original licence link has changed is not relivant.
4731  *
4732  * Fork - LGPL
4733  * <script type="text/javascript">
4734  */
4735
4736 /**
4737  * @class Roo.JsonView
4738  * @extends Roo.View
4739  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
4740 <pre><code>
4741 var view = new Roo.JsonView({
4742     container: "my-element",
4743     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
4744     multiSelect: true, 
4745     jsonRoot: "data" 
4746 });
4747
4748 // listen for node click?
4749 view.on("click", function(vw, index, node, e){
4750     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
4751 });
4752
4753 // direct load of JSON data
4754 view.load("foobar.php");
4755
4756 // Example from my blog list
4757 var tpl = new Roo.Template(
4758     '&lt;div class="entry"&gt;' +
4759     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
4760     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
4761     "&lt;/div&gt;&lt;hr /&gt;"
4762 );
4763
4764 var moreView = new Roo.JsonView({
4765     container :  "entry-list", 
4766     template : tpl,
4767     jsonRoot: "posts"
4768 });
4769 moreView.on("beforerender", this.sortEntries, this);
4770 moreView.load({
4771     url: "/blog/get-posts.php",
4772     params: "allposts=true",
4773     text: "Loading Blog Entries..."
4774 });
4775 </code></pre>
4776
4777 * Note: old code is supported with arguments : (container, template, config)
4778
4779
4780  * @constructor
4781  * Create a new JsonView
4782  * 
4783  * @param {Object} config The config object
4784  * 
4785  */
4786 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
4787     
4788     
4789     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
4790
4791     var um = this.el.getUpdateManager();
4792     um.setRenderer(this);
4793     um.on("update", this.onLoad, this);
4794     um.on("failure", this.onLoadException, this);
4795
4796     /**
4797      * @event beforerender
4798      * Fires before rendering of the downloaded JSON data.
4799      * @param {Roo.JsonView} this
4800      * @param {Object} data The JSON data loaded
4801      */
4802     /**
4803      * @event load
4804      * Fires when data is loaded.
4805      * @param {Roo.JsonView} this
4806      * @param {Object} data The JSON data loaded
4807      * @param {Object} response The raw Connect response object
4808      */
4809     /**
4810      * @event loadexception
4811      * Fires when loading fails.
4812      * @param {Roo.JsonView} this
4813      * @param {Object} response The raw Connect response object
4814      */
4815     this.addEvents({
4816         'beforerender' : true,
4817         'load' : true,
4818         'loadexception' : true
4819     });
4820 };
4821 Roo.extend(Roo.JsonView, Roo.View, {
4822     /**
4823      * @type {String} The root property in the loaded JSON object that contains the data
4824      */
4825     jsonRoot : "",
4826
4827     /**
4828      * Refreshes the view.
4829      */
4830     refresh : function(){
4831         this.clearSelections();
4832         this.el.update("");
4833         var html = [];
4834         var o = this.jsonData;
4835         if(o && o.length > 0){
4836             for(var i = 0, len = o.length; i < len; i++){
4837                 var data = this.prepareData(o[i], i, o);
4838                 html[html.length] = this.tpl.apply(data);
4839             }
4840         }else{
4841             html.push(this.emptyText);
4842         }
4843         this.el.update(html.join(""));
4844         this.nodes = this.el.dom.childNodes;
4845         this.updateIndexes(0);
4846     },
4847
4848     /**
4849      * 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.
4850      * @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:
4851      <pre><code>
4852      view.load({
4853          url: "your-url.php",
4854          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
4855          callback: yourFunction,
4856          scope: yourObject, //(optional scope)
4857          discardUrl: false,
4858          nocache: false,
4859          text: "Loading...",
4860          timeout: 30,
4861          scripts: false
4862      });
4863      </code></pre>
4864      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
4865      * 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.
4866      * @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}
4867      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
4868      * @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.
4869      */
4870     load : function(){
4871         var um = this.el.getUpdateManager();
4872         um.update.apply(um, arguments);
4873     },
4874
4875     // note - render is a standard framework call...
4876     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
4877     render : function(el, response){
4878         
4879         this.clearSelections();
4880         this.el.update("");
4881         var o;
4882         try{
4883             if (response != '') {
4884                 o = Roo.util.JSON.decode(response.responseText);
4885                 if(this.jsonRoot){
4886                     
4887                     o = o[this.jsonRoot];
4888                 }
4889             }
4890         } catch(e){
4891         }
4892         /**
4893          * The current JSON data or null
4894          */
4895         this.jsonData = o;
4896         this.beforeRender();
4897         this.refresh();
4898     },
4899
4900 /**
4901  * Get the number of records in the current JSON dataset
4902  * @return {Number}
4903  */
4904     getCount : function(){
4905         return this.jsonData ? this.jsonData.length : 0;
4906     },
4907
4908 /**
4909  * Returns the JSON object for the specified node(s)
4910  * @param {HTMLElement/Array} node The node or an array of nodes
4911  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
4912  * you get the JSON object for the node
4913  */
4914     getNodeData : function(node){
4915         if(node instanceof Array){
4916             var data = [];
4917             for(var i = 0, len = node.length; i < len; i++){
4918                 data.push(this.getNodeData(node[i]));
4919             }
4920             return data;
4921         }
4922         return this.jsonData[this.indexOf(node)] || null;
4923     },
4924
4925     beforeRender : function(){
4926         this.snapshot = this.jsonData;
4927         if(this.sortInfo){
4928             this.sort.apply(this, this.sortInfo);
4929         }
4930         this.fireEvent("beforerender", this, this.jsonData);
4931     },
4932
4933     onLoad : function(el, o){
4934         this.fireEvent("load", this, this.jsonData, o);
4935     },
4936
4937     onLoadException : function(el, o){
4938         this.fireEvent("loadexception", this, o);
4939     },
4940
4941 /**
4942  * Filter the data by a specific property.
4943  * @param {String} property A property on your JSON objects
4944  * @param {String/RegExp} value Either string that the property values
4945  * should start with, or a RegExp to test against the property
4946  */
4947     filter : function(property, value){
4948         if(this.jsonData){
4949             var data = [];
4950             var ss = this.snapshot;
4951             if(typeof value == "string"){
4952                 var vlen = value.length;
4953                 if(vlen == 0){
4954                     this.clearFilter();
4955                     return;
4956                 }
4957                 value = value.toLowerCase();
4958                 for(var i = 0, len = ss.length; i < len; i++){
4959                     var o = ss[i];
4960                     if(o[property].substr(0, vlen).toLowerCase() == value){
4961                         data.push(o);
4962                     }
4963                 }
4964             } else if(value.exec){ // regex?
4965                 for(var i = 0, len = ss.length; i < len; i++){
4966                     var o = ss[i];
4967                     if(value.test(o[property])){
4968                         data.push(o);
4969                     }
4970                 }
4971             } else{
4972                 return;
4973             }
4974             this.jsonData = data;
4975             this.refresh();
4976         }
4977     },
4978
4979 /**
4980  * Filter by a function. The passed function will be called with each
4981  * object in the current dataset. If the function returns true the value is kept,
4982  * otherwise it is filtered.
4983  * @param {Function} fn
4984  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
4985  */
4986     filterBy : function(fn, scope){
4987         if(this.jsonData){
4988             var data = [];
4989             var ss = this.snapshot;
4990             for(var i = 0, len = ss.length; i < len; i++){
4991                 var o = ss[i];
4992                 if(fn.call(scope || this, o)){
4993                     data.push(o);
4994                 }
4995             }
4996             this.jsonData = data;
4997             this.refresh();
4998         }
4999     },
5000
5001 /**
5002  * Clears the current filter.
5003  */
5004     clearFilter : function(){
5005         if(this.snapshot && this.jsonData != this.snapshot){
5006             this.jsonData = this.snapshot;
5007             this.refresh();
5008         }
5009     },
5010
5011
5012 /**
5013  * Sorts the data for this view and refreshes it.
5014  * @param {String} property A property on your JSON objects to sort on
5015  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
5016  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
5017  */
5018     sort : function(property, dir, sortType){
5019         this.sortInfo = Array.prototype.slice.call(arguments, 0);
5020         if(this.jsonData){
5021             var p = property;
5022             var dsc = dir && dir.toLowerCase() == "desc";
5023             var f = function(o1, o2){
5024                 var v1 = sortType ? sortType(o1[p]) : o1[p];
5025                 var v2 = sortType ? sortType(o2[p]) : o2[p];
5026                 ;
5027                 if(v1 < v2){
5028                     return dsc ? +1 : -1;
5029                 } else if(v1 > v2){
5030                     return dsc ? -1 : +1;
5031                 } else{
5032                     return 0;
5033                 }
5034             };
5035             this.jsonData.sort(f);
5036             this.refresh();
5037             if(this.jsonData != this.snapshot){
5038                 this.snapshot.sort(f);
5039             }
5040         }
5041     }
5042 });/*
5043  * Based on:
5044  * Ext JS Library 1.1.1
5045  * Copyright(c) 2006-2007, Ext JS, LLC.
5046  *
5047  * Originally Released Under LGPL - original licence link has changed is not relivant.
5048  *
5049  * Fork - LGPL
5050  * <script type="text/javascript">
5051  */
5052  
5053
5054 /**
5055  * @class Roo.ColorPalette
5056  * @extends Roo.Component
5057  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
5058  * Here's an example of typical usage:
5059  * <pre><code>
5060 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
5061 cp.render('my-div');
5062
5063 cp.on('select', function(palette, selColor){
5064     // do something with selColor
5065 });
5066 </code></pre>
5067  * @constructor
5068  * Create a new ColorPalette
5069  * @param {Object} config The config object
5070  */
5071 Roo.ColorPalette = function(config){
5072     Roo.ColorPalette.superclass.constructor.call(this, config);
5073     this.addEvents({
5074         /**
5075              * @event select
5076              * Fires when a color is selected
5077              * @param {ColorPalette} this
5078              * @param {String} color The 6-digit color hex code (without the # symbol)
5079              */
5080         select: true
5081     });
5082
5083     if(this.handler){
5084         this.on("select", this.handler, this.scope, true);
5085     }
5086 };
5087 Roo.extend(Roo.ColorPalette, Roo.Component, {
5088     /**
5089      * @cfg {String} itemCls
5090      * The CSS class to apply to the containing element (defaults to "x-color-palette")
5091      */
5092     itemCls : "x-color-palette",
5093     /**
5094      * @cfg {String} value
5095      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
5096      * the hex codes are case-sensitive.
5097      */
5098     value : null,
5099     clickEvent:'click',
5100     // private
5101     ctype: "Roo.ColorPalette",
5102
5103     /**
5104      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
5105      */
5106     allowReselect : false,
5107
5108     /**
5109      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
5110      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
5111      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
5112      * of colors with the width setting until the box is symmetrical.</p>
5113      * <p>You can override individual colors if needed:</p>
5114      * <pre><code>
5115 var cp = new Roo.ColorPalette();
5116 cp.colors[0] = "FF0000";  // change the first box to red
5117 </code></pre>
5118
5119 Or you can provide a custom array of your own for complete control:
5120 <pre><code>
5121 var cp = new Roo.ColorPalette();
5122 cp.colors = ["000000", "993300", "333300"];
5123 </code></pre>
5124      * @type Array
5125      */
5126     colors : [
5127         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
5128         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
5129         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
5130         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
5131         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
5132     ],
5133
5134     // private
5135     onRender : function(container, position){
5136         var t = new Roo.MasterTemplate(
5137             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
5138         );
5139         var c = this.colors;
5140         for(var i = 0, len = c.length; i < len; i++){
5141             t.add([c[i]]);
5142         }
5143         var el = document.createElement("div");
5144         el.className = this.itemCls;
5145         t.overwrite(el);
5146         container.dom.insertBefore(el, position);
5147         this.el = Roo.get(el);
5148         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
5149         if(this.clickEvent != 'click'){
5150             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
5151         }
5152     },
5153
5154     // private
5155     afterRender : function(){
5156         Roo.ColorPalette.superclass.afterRender.call(this);
5157         if(this.value){
5158             var s = this.value;
5159             this.value = null;
5160             this.select(s);
5161         }
5162     },
5163
5164     // private
5165     handleClick : function(e, t){
5166         e.preventDefault();
5167         if(!this.disabled){
5168             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
5169             this.select(c.toUpperCase());
5170         }
5171     },
5172
5173     /**
5174      * Selects the specified color in the palette (fires the select event)
5175      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
5176      */
5177     select : function(color){
5178         color = color.replace("#", "");
5179         if(color != this.value || this.allowReselect){
5180             var el = this.el;
5181             if(this.value){
5182                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
5183             }
5184             el.child("a.color-"+color).addClass("x-color-palette-sel");
5185             this.value = color;
5186             this.fireEvent("select", this, color);
5187         }
5188     }
5189 });/*
5190  * Based on:
5191  * Ext JS Library 1.1.1
5192  * Copyright(c) 2006-2007, Ext JS, LLC.
5193  *
5194  * Originally Released Under LGPL - original licence link has changed is not relivant.
5195  *
5196  * Fork - LGPL
5197  * <script type="text/javascript">
5198  */
5199  
5200 /**
5201  * @class Roo.DatePicker
5202  * @extends Roo.Component
5203  * Simple date picker class.
5204  * @constructor
5205  * Create a new DatePicker
5206  * @param {Object} config The config object
5207  */
5208 Roo.DatePicker = function(config){
5209     Roo.DatePicker.superclass.constructor.call(this, config);
5210
5211     this.value = config && config.value ?
5212                  config.value.clearTime() : new Date().clearTime();
5213
5214     this.addEvents({
5215         /**
5216              * @event select
5217              * Fires when a date is selected
5218              * @param {DatePicker} this
5219              * @param {Date} date The selected date
5220              */
5221         'select': true,
5222         /**
5223              * @event monthchange
5224              * Fires when the displayed month changes 
5225              * @param {DatePicker} this
5226              * @param {Date} date The selected month
5227              */
5228         'monthchange': true
5229     });
5230
5231     if(this.handler){
5232         this.on("select", this.handler,  this.scope || this);
5233     }
5234     // build the disabledDatesRE
5235     if(!this.disabledDatesRE && this.disabledDates){
5236         var dd = this.disabledDates;
5237         var re = "(?:";
5238         for(var i = 0; i < dd.length; i++){
5239             re += dd[i];
5240             if(i != dd.length-1) {
5241                 re += "|";
5242             }
5243         }
5244         this.disabledDatesRE = new RegExp(re + ")");
5245     }
5246 };
5247
5248 Roo.extend(Roo.DatePicker, Roo.Component, {
5249     /**
5250      * @cfg {String} todayText
5251      * The text to display on the button that selects the current date (defaults to "Today")
5252      */
5253     todayText : "Today",
5254     /**
5255      * @cfg {String} okText
5256      * The text to display on the ok button
5257      */
5258     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
5259     /**
5260      * @cfg {String} cancelText
5261      * The text to display on the cancel button
5262      */
5263     cancelText : "Cancel",
5264     /**
5265      * @cfg {String} todayTip
5266      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
5267      */
5268     todayTip : "{0} (Spacebar)",
5269     /**
5270      * @cfg {Date} minDate
5271      * Minimum allowable date (JavaScript date object, defaults to null)
5272      */
5273     minDate : null,
5274     /**
5275      * @cfg {Date} maxDate
5276      * Maximum allowable date (JavaScript date object, defaults to null)
5277      */
5278     maxDate : null,
5279     /**
5280      * @cfg {String} minText
5281      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
5282      */
5283     minText : "This date is before the minimum date",
5284     /**
5285      * @cfg {String} maxText
5286      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
5287      */
5288     maxText : "This date is after the maximum date",
5289     /**
5290      * @cfg {String} format
5291      * The default date format string which can be overriden for localization support.  The format must be
5292      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
5293      */
5294     format : "m/d/y",
5295     /**
5296      * @cfg {Array} disabledDays
5297      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
5298      */
5299     disabledDays : null,
5300     /**
5301      * @cfg {String} disabledDaysText
5302      * The tooltip to display when the date falls on a disabled day (defaults to "")
5303      */
5304     disabledDaysText : "",
5305     /**
5306      * @cfg {RegExp} disabledDatesRE
5307      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
5308      */
5309     disabledDatesRE : null,
5310     /**
5311      * @cfg {String} disabledDatesText
5312      * The tooltip text to display when the date falls on a disabled date (defaults to "")
5313      */
5314     disabledDatesText : "",
5315     /**
5316      * @cfg {Boolean} constrainToViewport
5317      * True to constrain the date picker to the viewport (defaults to true)
5318      */
5319     constrainToViewport : true,
5320     /**
5321      * @cfg {Array} monthNames
5322      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
5323      */
5324     monthNames : Date.monthNames,
5325     /**
5326      * @cfg {Array} dayNames
5327      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
5328      */
5329     dayNames : Date.dayNames,
5330     /**
5331      * @cfg {String} nextText
5332      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
5333      */
5334     nextText: 'Next Month (Control+Right)',
5335     /**
5336      * @cfg {String} prevText
5337      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
5338      */
5339     prevText: 'Previous Month (Control+Left)',
5340     /**
5341      * @cfg {String} monthYearText
5342      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
5343      */
5344     monthYearText: 'Choose a month (Control+Up/Down to move years)',
5345     /**
5346      * @cfg {Number} startDay
5347      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
5348      */
5349     startDay : 0,
5350     /**
5351      * @cfg {Bool} showClear
5352      * Show a clear button (usefull for date form elements that can be blank.)
5353      */
5354     
5355     showClear: false,
5356     
5357     /**
5358      * Sets the value of the date field
5359      * @param {Date} value The date to set
5360      */
5361     setValue : function(value){
5362         var old = this.value;
5363         
5364         if (typeof(value) == 'string') {
5365          
5366             value = Date.parseDate(value, this.format);
5367         }
5368         if (!value) {
5369             value = new Date();
5370         }
5371         
5372         this.value = value.clearTime(true);
5373         if(this.el){
5374             this.update(this.value);
5375         }
5376     },
5377
5378     /**
5379      * Gets the current selected value of the date field
5380      * @return {Date} The selected date
5381      */
5382     getValue : function(){
5383         return this.value;
5384     },
5385
5386     // private
5387     focus : function(){
5388         if(this.el){
5389             this.update(this.activeDate);
5390         }
5391     },
5392
5393     // privateval
5394     onRender : function(container, position){
5395         
5396         var m = [
5397              '<table cellspacing="0">',
5398                 '<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>',
5399                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
5400         var dn = this.dayNames;
5401         for(var i = 0; i < 7; i++){
5402             var d = this.startDay+i;
5403             if(d > 6){
5404                 d = d-7;
5405             }
5406             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
5407         }
5408         m[m.length] = "</tr></thead><tbody><tr>";
5409         for(var i = 0; i < 42; i++) {
5410             if(i % 7 == 0 && i != 0){
5411                 m[m.length] = "</tr><tr>";
5412             }
5413             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
5414         }
5415         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
5416             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
5417
5418         var el = document.createElement("div");
5419         el.className = "x-date-picker";
5420         el.innerHTML = m.join("");
5421
5422         container.dom.insertBefore(el, position);
5423
5424         this.el = Roo.get(el);
5425         this.eventEl = Roo.get(el.firstChild);
5426
5427         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
5428             handler: this.showPrevMonth,
5429             scope: this,
5430             preventDefault:true,
5431             stopDefault:true
5432         });
5433
5434         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
5435             handler: this.showNextMonth,
5436             scope: this,
5437             preventDefault:true,
5438             stopDefault:true
5439         });
5440
5441         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
5442
5443         this.monthPicker = this.el.down('div.x-date-mp');
5444         this.monthPicker.enableDisplayMode('block');
5445         
5446         var kn = new Roo.KeyNav(this.eventEl, {
5447             "left" : function(e){
5448                 e.ctrlKey ?
5449                     this.showPrevMonth() :
5450                     this.update(this.activeDate.add("d", -1));
5451             },
5452
5453             "right" : function(e){
5454                 e.ctrlKey ?
5455                     this.showNextMonth() :
5456                     this.update(this.activeDate.add("d", 1));
5457             },
5458
5459             "up" : function(e){
5460                 e.ctrlKey ?
5461                     this.showNextYear() :
5462                     this.update(this.activeDate.add("d", -7));
5463             },
5464
5465             "down" : function(e){
5466                 e.ctrlKey ?
5467                     this.showPrevYear() :
5468                     this.update(this.activeDate.add("d", 7));
5469             },
5470
5471             "pageUp" : function(e){
5472                 this.showNextMonth();
5473             },
5474
5475             "pageDown" : function(e){
5476                 this.showPrevMonth();
5477             },
5478
5479             "enter" : function(e){
5480                 e.stopPropagation();
5481                 return true;
5482             },
5483
5484             scope : this
5485         });
5486
5487         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
5488
5489         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
5490
5491         this.el.unselectable();
5492         
5493         this.cells = this.el.select("table.x-date-inner tbody td");
5494         this.textNodes = this.el.query("table.x-date-inner tbody span");
5495
5496         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
5497             text: "&#160;",
5498             tooltip: this.monthYearText
5499         });
5500
5501         this.mbtn.on('click', this.showMonthPicker, this);
5502         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
5503
5504
5505         var today = (new Date()).dateFormat(this.format);
5506         
5507         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
5508         if (this.showClear) {
5509             baseTb.add( new Roo.Toolbar.Fill());
5510         }
5511         baseTb.add({
5512             text: String.format(this.todayText, today),
5513             tooltip: String.format(this.todayTip, today),
5514             handler: this.selectToday,
5515             scope: this
5516         });
5517         
5518         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
5519             
5520         //});
5521         if (this.showClear) {
5522             
5523             baseTb.add( new Roo.Toolbar.Fill());
5524             baseTb.add({
5525                 text: '&#160;',
5526                 cls: 'x-btn-icon x-btn-clear',
5527                 handler: function() {
5528                     //this.value = '';
5529                     this.fireEvent("select", this, '');
5530                 },
5531                 scope: this
5532             });
5533         }
5534         
5535         
5536         if(Roo.isIE){
5537             this.el.repaint();
5538         }
5539         this.update(this.value);
5540     },
5541
5542     createMonthPicker : function(){
5543         if(!this.monthPicker.dom.firstChild){
5544             var buf = ['<table border="0" cellspacing="0">'];
5545             for(var i = 0; i < 6; i++){
5546                 buf.push(
5547                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
5548                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
5549                     i == 0 ?
5550                     '<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>' :
5551                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
5552                 );
5553             }
5554             buf.push(
5555                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
5556                     this.okText,
5557                     '</button><button type="button" class="x-date-mp-cancel">',
5558                     this.cancelText,
5559                     '</button></td></tr>',
5560                 '</table>'
5561             );
5562             this.monthPicker.update(buf.join(''));
5563             this.monthPicker.on('click', this.onMonthClick, this);
5564             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
5565
5566             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
5567             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
5568
5569             this.mpMonths.each(function(m, a, i){
5570                 i += 1;
5571                 if((i%2) == 0){
5572                     m.dom.xmonth = 5 + Math.round(i * .5);
5573                 }else{
5574                     m.dom.xmonth = Math.round((i-1) * .5);
5575                 }
5576             });
5577         }
5578     },
5579
5580     showMonthPicker : function(){
5581         this.createMonthPicker();
5582         var size = this.el.getSize();
5583         this.monthPicker.setSize(size);
5584         this.monthPicker.child('table').setSize(size);
5585
5586         this.mpSelMonth = (this.activeDate || this.value).getMonth();
5587         this.updateMPMonth(this.mpSelMonth);
5588         this.mpSelYear = (this.activeDate || this.value).getFullYear();
5589         this.updateMPYear(this.mpSelYear);
5590
5591         this.monthPicker.slideIn('t', {duration:.2});
5592     },
5593
5594     updateMPYear : function(y){
5595         this.mpyear = y;
5596         var ys = this.mpYears.elements;
5597         for(var i = 1; i <= 10; i++){
5598             var td = ys[i-1], y2;
5599             if((i%2) == 0){
5600                 y2 = y + Math.round(i * .5);
5601                 td.firstChild.innerHTML = y2;
5602                 td.xyear = y2;
5603             }else{
5604                 y2 = y - (5-Math.round(i * .5));
5605                 td.firstChild.innerHTML = y2;
5606                 td.xyear = y2;
5607             }
5608             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
5609         }
5610     },
5611
5612     updateMPMonth : function(sm){
5613         this.mpMonths.each(function(m, a, i){
5614             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
5615         });
5616     },
5617
5618     selectMPMonth: function(m){
5619         
5620     },
5621
5622     onMonthClick : function(e, t){
5623         e.stopEvent();
5624         var el = new Roo.Element(t), pn;
5625         if(el.is('button.x-date-mp-cancel')){
5626             this.hideMonthPicker();
5627         }
5628         else if(el.is('button.x-date-mp-ok')){
5629             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5630             this.hideMonthPicker();
5631         }
5632         else if(pn = el.up('td.x-date-mp-month', 2)){
5633             this.mpMonths.removeClass('x-date-mp-sel');
5634             pn.addClass('x-date-mp-sel');
5635             this.mpSelMonth = pn.dom.xmonth;
5636         }
5637         else if(pn = el.up('td.x-date-mp-year', 2)){
5638             this.mpYears.removeClass('x-date-mp-sel');
5639             pn.addClass('x-date-mp-sel');
5640             this.mpSelYear = pn.dom.xyear;
5641         }
5642         else if(el.is('a.x-date-mp-prev')){
5643             this.updateMPYear(this.mpyear-10);
5644         }
5645         else if(el.is('a.x-date-mp-next')){
5646             this.updateMPYear(this.mpyear+10);
5647         }
5648     },
5649
5650     onMonthDblClick : function(e, t){
5651         e.stopEvent();
5652         var el = new Roo.Element(t), pn;
5653         if(pn = el.up('td.x-date-mp-month', 2)){
5654             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
5655             this.hideMonthPicker();
5656         }
5657         else if(pn = el.up('td.x-date-mp-year', 2)){
5658             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5659             this.hideMonthPicker();
5660         }
5661     },
5662
5663     hideMonthPicker : function(disableAnim){
5664         if(this.monthPicker){
5665             if(disableAnim === true){
5666                 this.monthPicker.hide();
5667             }else{
5668                 this.monthPicker.slideOut('t', {duration:.2});
5669             }
5670         }
5671     },
5672
5673     // private
5674     showPrevMonth : function(e){
5675         this.update(this.activeDate.add("mo", -1));
5676     },
5677
5678     // private
5679     showNextMonth : function(e){
5680         this.update(this.activeDate.add("mo", 1));
5681     },
5682
5683     // private
5684     showPrevYear : function(){
5685         this.update(this.activeDate.add("y", -1));
5686     },
5687
5688     // private
5689     showNextYear : function(){
5690         this.update(this.activeDate.add("y", 1));
5691     },
5692
5693     // private
5694     handleMouseWheel : function(e){
5695         var delta = e.getWheelDelta();
5696         if(delta > 0){
5697             this.showPrevMonth();
5698             e.stopEvent();
5699         } else if(delta < 0){
5700             this.showNextMonth();
5701             e.stopEvent();
5702         }
5703     },
5704
5705     // private
5706     handleDateClick : function(e, t){
5707         e.stopEvent();
5708         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
5709             this.setValue(new Date(t.dateValue));
5710             this.fireEvent("select", this, this.value);
5711         }
5712     },
5713
5714     // private
5715     selectToday : function(){
5716         this.setValue(new Date().clearTime());
5717         this.fireEvent("select", this, this.value);
5718     },
5719
5720     // private
5721     update : function(date)
5722     {
5723         var vd = this.activeDate;
5724         this.activeDate = date;
5725         if(vd && this.el){
5726             var t = date.getTime();
5727             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
5728                 this.cells.removeClass("x-date-selected");
5729                 this.cells.each(function(c){
5730                    if(c.dom.firstChild.dateValue == t){
5731                        c.addClass("x-date-selected");
5732                        setTimeout(function(){
5733                             try{c.dom.firstChild.focus();}catch(e){}
5734                        }, 50);
5735                        return false;
5736                    }
5737                 });
5738                 return;
5739             }
5740         }
5741         
5742         var days = date.getDaysInMonth();
5743         var firstOfMonth = date.getFirstDateOfMonth();
5744         var startingPos = firstOfMonth.getDay()-this.startDay;
5745
5746         if(startingPos <= this.startDay){
5747             startingPos += 7;
5748         }
5749
5750         var pm = date.add("mo", -1);
5751         var prevStart = pm.getDaysInMonth()-startingPos;
5752
5753         var cells = this.cells.elements;
5754         var textEls = this.textNodes;
5755         days += startingPos;
5756
5757         // convert everything to numbers so it's fast
5758         var day = 86400000;
5759         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
5760         var today = new Date().clearTime().getTime();
5761         var sel = date.clearTime().getTime();
5762         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
5763         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
5764         var ddMatch = this.disabledDatesRE;
5765         var ddText = this.disabledDatesText;
5766         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
5767         var ddaysText = this.disabledDaysText;
5768         var format = this.format;
5769
5770         var setCellClass = function(cal, cell){
5771             cell.title = "";
5772             var t = d.getTime();
5773             cell.firstChild.dateValue = t;
5774             if(t == today){
5775                 cell.className += " x-date-today";
5776                 cell.title = cal.todayText;
5777             }
5778             if(t == sel){
5779                 cell.className += " x-date-selected";
5780                 setTimeout(function(){
5781                     try{cell.firstChild.focus();}catch(e){}
5782                 }, 50);
5783             }
5784             // disabling
5785             if(t < min) {
5786                 cell.className = " x-date-disabled";
5787                 cell.title = cal.minText;
5788                 return;
5789             }
5790             if(t > max) {
5791                 cell.className = " x-date-disabled";
5792                 cell.title = cal.maxText;
5793                 return;
5794             }
5795             if(ddays){
5796                 if(ddays.indexOf(d.getDay()) != -1){
5797                     cell.title = ddaysText;
5798                     cell.className = " x-date-disabled";
5799                 }
5800             }
5801             if(ddMatch && format){
5802                 var fvalue = d.dateFormat(format);
5803                 if(ddMatch.test(fvalue)){
5804                     cell.title = ddText.replace("%0", fvalue);
5805                     cell.className = " x-date-disabled";
5806                 }
5807             }
5808         };
5809
5810         var i = 0;
5811         for(; i < startingPos; i++) {
5812             textEls[i].innerHTML = (++prevStart);
5813             d.setDate(d.getDate()+1);
5814             cells[i].className = "x-date-prevday";
5815             setCellClass(this, cells[i]);
5816         }
5817         for(; i < days; i++){
5818             intDay = i - startingPos + 1;
5819             textEls[i].innerHTML = (intDay);
5820             d.setDate(d.getDate()+1);
5821             cells[i].className = "x-date-active";
5822             setCellClass(this, cells[i]);
5823         }
5824         var extraDays = 0;
5825         for(; i < 42; i++) {
5826              textEls[i].innerHTML = (++extraDays);
5827              d.setDate(d.getDate()+1);
5828              cells[i].className = "x-date-nextday";
5829              setCellClass(this, cells[i]);
5830         }
5831
5832         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
5833         this.fireEvent('monthchange', this, date);
5834         
5835         if(!this.internalRender){
5836             var main = this.el.dom.firstChild;
5837             var w = main.offsetWidth;
5838             this.el.setWidth(w + this.el.getBorderWidth("lr"));
5839             Roo.fly(main).setWidth(w);
5840             this.internalRender = true;
5841             // opera does not respect the auto grow header center column
5842             // then, after it gets a width opera refuses to recalculate
5843             // without a second pass
5844             if(Roo.isOpera && !this.secondPass){
5845                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
5846                 this.secondPass = true;
5847                 this.update.defer(10, this, [date]);
5848             }
5849         }
5850         
5851         
5852     }
5853 });        /*
5854  * Based on:
5855  * Ext JS Library 1.1.1
5856  * Copyright(c) 2006-2007, Ext JS, LLC.
5857  *
5858  * Originally Released Under LGPL - original licence link has changed is not relivant.
5859  *
5860  * Fork - LGPL
5861  * <script type="text/javascript">
5862  */
5863 /**
5864  * @class Roo.TabPanel
5865  * @extends Roo.util.Observable
5866  * A lightweight tab container.
5867  * <br><br>
5868  * Usage:
5869  * <pre><code>
5870 // basic tabs 1, built from existing content
5871 var tabs = new Roo.TabPanel("tabs1");
5872 tabs.addTab("script", "View Script");
5873 tabs.addTab("markup", "View Markup");
5874 tabs.activate("script");
5875
5876 // more advanced tabs, built from javascript
5877 var jtabs = new Roo.TabPanel("jtabs");
5878 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
5879
5880 // set up the UpdateManager
5881 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
5882 var updater = tab2.getUpdateManager();
5883 updater.setDefaultUrl("ajax1.htm");
5884 tab2.on('activate', updater.refresh, updater, true);
5885
5886 // Use setUrl for Ajax loading
5887 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
5888 tab3.setUrl("ajax2.htm", null, true);
5889
5890 // Disabled tab
5891 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
5892 tab4.disable();
5893
5894 jtabs.activate("jtabs-1");
5895  * </code></pre>
5896  * @constructor
5897  * Create a new TabPanel.
5898  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
5899  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
5900  */
5901 Roo.TabPanel = function(container, config){
5902     /**
5903     * The container element for this TabPanel.
5904     * @type Roo.Element
5905     */
5906     this.el = Roo.get(container, true);
5907     if(config){
5908         if(typeof config == "boolean"){
5909             this.tabPosition = config ? "bottom" : "top";
5910         }else{
5911             Roo.apply(this, config);
5912         }
5913     }
5914     if(this.tabPosition == "bottom"){
5915         this.bodyEl = Roo.get(this.createBody(this.el.dom));
5916         this.el.addClass("x-tabs-bottom");
5917     }
5918     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
5919     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
5920     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
5921     if(Roo.isIE){
5922         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
5923     }
5924     if(this.tabPosition != "bottom"){
5925         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
5926          * @type Roo.Element
5927          */
5928         this.bodyEl = Roo.get(this.createBody(this.el.dom));
5929         this.el.addClass("x-tabs-top");
5930     }
5931     this.items = [];
5932
5933     this.bodyEl.setStyle("position", "relative");
5934
5935     this.active = null;
5936     this.activateDelegate = this.activate.createDelegate(this);
5937
5938     this.addEvents({
5939         /**
5940          * @event tabchange
5941          * Fires when the active tab changes
5942          * @param {Roo.TabPanel} this
5943          * @param {Roo.TabPanelItem} activePanel The new active tab
5944          */
5945         "tabchange": true,
5946         /**
5947          * @event beforetabchange
5948          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
5949          * @param {Roo.TabPanel} this
5950          * @param {Object} e Set cancel to true on this object to cancel the tab change
5951          * @param {Roo.TabPanelItem} tab The tab being changed to
5952          */
5953         "beforetabchange" : true
5954     });
5955
5956     Roo.EventManager.onWindowResize(this.onResize, this);
5957     this.cpad = this.el.getPadding("lr");
5958     this.hiddenCount = 0;
5959
5960
5961     // toolbar on the tabbar support...
5962     if (this.toolbar) {
5963         var tcfg = this.toolbar;
5964         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
5965         this.toolbar = new Roo.Toolbar(tcfg);
5966         if (Roo.isSafari) {
5967             var tbl = tcfg.container.child('table', true);
5968             tbl.setAttribute('width', '100%');
5969         }
5970         
5971     }
5972    
5973
5974
5975     Roo.TabPanel.superclass.constructor.call(this);
5976 };
5977
5978 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
5979     /*
5980      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
5981      */
5982     tabPosition : "top",
5983     /*
5984      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
5985      */
5986     currentTabWidth : 0,
5987     /*
5988      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
5989      */
5990     minTabWidth : 40,
5991     /*
5992      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
5993      */
5994     maxTabWidth : 250,
5995     /*
5996      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
5997      */
5998     preferredTabWidth : 175,
5999     /*
6000      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
6001      */
6002     resizeTabs : false,
6003     /*
6004      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
6005      */
6006     monitorResize : true,
6007     /*
6008      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
6009      */
6010     toolbar : false,
6011
6012     /**
6013      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
6014      * @param {String} id The id of the div to use <b>or create</b>
6015      * @param {String} text The text for the tab
6016      * @param {String} content (optional) Content to put in the TabPanelItem body
6017      * @param {Boolean} closable (optional) True to create a close icon on the tab
6018      * @return {Roo.TabPanelItem} The created TabPanelItem
6019      */
6020     addTab : function(id, text, content, closable){
6021         var item = new Roo.TabPanelItem(this, id, text, closable);
6022         this.addTabItem(item);
6023         if(content){
6024             item.setContent(content);
6025         }
6026         return item;
6027     },
6028
6029     /**
6030      * Returns the {@link Roo.TabPanelItem} with the specified id/index
6031      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
6032      * @return {Roo.TabPanelItem}
6033      */
6034     getTab : function(id){
6035         return this.items[id];
6036     },
6037
6038     /**
6039      * Hides the {@link Roo.TabPanelItem} with the specified id/index
6040      * @param {String/Number} id The id or index of the TabPanelItem to hide.
6041      */
6042     hideTab : function(id){
6043         var t = this.items[id];
6044         if(!t.isHidden()){
6045            t.setHidden(true);
6046            this.hiddenCount++;
6047            this.autoSizeTabs();
6048         }
6049     },
6050
6051     /**
6052      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
6053      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
6054      */
6055     unhideTab : function(id){
6056         var t = this.items[id];
6057         if(t.isHidden()){
6058            t.setHidden(false);
6059            this.hiddenCount--;
6060            this.autoSizeTabs();
6061         }
6062     },
6063
6064     /**
6065      * Adds an existing {@link Roo.TabPanelItem}.
6066      * @param {Roo.TabPanelItem} item The TabPanelItem to add
6067      */
6068     addTabItem : function(item){
6069         this.items[item.id] = item;
6070         this.items.push(item);
6071         if(this.resizeTabs){
6072            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
6073            this.autoSizeTabs();
6074         }else{
6075             item.autoSize();
6076         }
6077     },
6078
6079     /**
6080      * Removes a {@link Roo.TabPanelItem}.
6081      * @param {String/Number} id The id or index of the TabPanelItem to remove.
6082      */
6083     removeTab : function(id){
6084         var items = this.items;
6085         var tab = items[id];
6086         if(!tab) { return; }
6087         var index = items.indexOf(tab);
6088         if(this.active == tab && items.length > 1){
6089             var newTab = this.getNextAvailable(index);
6090             if(newTab) {
6091                 newTab.activate();
6092             }
6093         }
6094         this.stripEl.dom.removeChild(tab.pnode.dom);
6095         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
6096             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
6097         }
6098         items.splice(index, 1);
6099         delete this.items[tab.id];
6100         tab.fireEvent("close", tab);
6101         tab.purgeListeners();
6102         this.autoSizeTabs();
6103     },
6104
6105     getNextAvailable : function(start){
6106         var items = this.items;
6107         var index = start;
6108         // look for a next tab that will slide over to
6109         // replace the one being removed
6110         while(index < items.length){
6111             var item = items[++index];
6112             if(item && !item.isHidden()){
6113                 return item;
6114             }
6115         }
6116         // if one isn't found select the previous tab (on the left)
6117         index = start;
6118         while(index >= 0){
6119             var item = items[--index];
6120             if(item && !item.isHidden()){
6121                 return item;
6122             }
6123         }
6124         return null;
6125     },
6126
6127     /**
6128      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
6129      * @param {String/Number} id The id or index of the TabPanelItem to disable.
6130      */
6131     disableTab : function(id){
6132         var tab = this.items[id];
6133         if(tab && this.active != tab){
6134             tab.disable();
6135         }
6136     },
6137
6138     /**
6139      * Enables a {@link Roo.TabPanelItem} that is disabled.
6140      * @param {String/Number} id The id or index of the TabPanelItem to enable.
6141      */
6142     enableTab : function(id){
6143         var tab = this.items[id];
6144         tab.enable();
6145     },
6146
6147     /**
6148      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
6149      * @param {String/Number} id The id or index of the TabPanelItem to activate.
6150      * @return {Roo.TabPanelItem} The TabPanelItem.
6151      */
6152     activate : function(id){
6153         var tab = this.items[id];
6154         if(!tab){
6155             return null;
6156         }
6157         if(tab == this.active || tab.disabled){
6158             return tab;
6159         }
6160         var e = {};
6161         this.fireEvent("beforetabchange", this, e, tab);
6162         if(e.cancel !== true && !tab.disabled){
6163             if(this.active){
6164                 this.active.hide();
6165             }
6166             this.active = this.items[id];
6167             this.active.show();
6168             this.fireEvent("tabchange", this, this.active);
6169         }
6170         return tab;
6171     },
6172
6173     /**
6174      * Gets the active {@link Roo.TabPanelItem}.
6175      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
6176      */
6177     getActiveTab : function(){
6178         return this.active;
6179     },
6180
6181     /**
6182      * Updates the tab body element to fit the height of the container element
6183      * for overflow scrolling
6184      * @param {Number} targetHeight (optional) Override the starting height from the elements height
6185      */
6186     syncHeight : function(targetHeight){
6187         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
6188         var bm = this.bodyEl.getMargins();
6189         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
6190         this.bodyEl.setHeight(newHeight);
6191         return newHeight;
6192     },
6193
6194     onResize : function(){
6195         if(this.monitorResize){
6196             this.autoSizeTabs();
6197         }
6198     },
6199
6200     /**
6201      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
6202      */
6203     beginUpdate : function(){
6204         this.updating = true;
6205     },
6206
6207     /**
6208      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
6209      */
6210     endUpdate : function(){
6211         this.updating = false;
6212         this.autoSizeTabs();
6213     },
6214
6215     /**
6216      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
6217      */
6218     autoSizeTabs : function(){
6219         var count = this.items.length;
6220         var vcount = count - this.hiddenCount;
6221         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
6222             return;
6223         }
6224         var w = Math.max(this.el.getWidth() - this.cpad, 10);
6225         var availWidth = Math.floor(w / vcount);
6226         var b = this.stripBody;
6227         if(b.getWidth() > w){
6228             var tabs = this.items;
6229             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
6230             if(availWidth < this.minTabWidth){
6231                 /*if(!this.sleft){    // incomplete scrolling code
6232                     this.createScrollButtons();
6233                 }
6234                 this.showScroll();
6235                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
6236             }
6237         }else{
6238             if(this.currentTabWidth < this.preferredTabWidth){
6239                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
6240             }
6241         }
6242     },
6243
6244     /**
6245      * Returns the number of tabs in this TabPanel.
6246      * @return {Number}
6247      */
6248      getCount : function(){
6249          return this.items.length;
6250      },
6251
6252     /**
6253      * Resizes all the tabs to the passed width
6254      * @param {Number} The new width
6255      */
6256     setTabWidth : function(width){
6257         this.currentTabWidth = width;
6258         for(var i = 0, len = this.items.length; i < len; i++) {
6259                 if(!this.items[i].isHidden()) {
6260                 this.items[i].setWidth(width);
6261             }
6262         }
6263     },
6264
6265     /**
6266      * Destroys this TabPanel
6267      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
6268      */
6269     destroy : function(removeEl){
6270         Roo.EventManager.removeResizeListener(this.onResize, this);
6271         for(var i = 0, len = this.items.length; i < len; i++){
6272             this.items[i].purgeListeners();
6273         }
6274         if(removeEl === true){
6275             this.el.update("");
6276             this.el.remove();
6277         }
6278     }
6279 });
6280
6281 /**
6282  * @class Roo.TabPanelItem
6283  * @extends Roo.util.Observable
6284  * Represents an individual item (tab plus body) in a TabPanel.
6285  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
6286  * @param {String} id The id of this TabPanelItem
6287  * @param {String} text The text for the tab of this TabPanelItem
6288  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
6289  */
6290 Roo.TabPanelItem = function(tabPanel, id, text, closable){
6291     /**
6292      * The {@link Roo.TabPanel} this TabPanelItem belongs to
6293      * @type Roo.TabPanel
6294      */
6295     this.tabPanel = tabPanel;
6296     /**
6297      * The id for this TabPanelItem
6298      * @type String
6299      */
6300     this.id = id;
6301     /** @private */
6302     this.disabled = false;
6303     /** @private */
6304     this.text = text;
6305     /** @private */
6306     this.loaded = false;
6307     this.closable = closable;
6308
6309     /**
6310      * The body element for this TabPanelItem.
6311      * @type Roo.Element
6312      */
6313     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
6314     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
6315     this.bodyEl.setStyle("display", "block");
6316     this.bodyEl.setStyle("zoom", "1");
6317     this.hideAction();
6318
6319     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
6320     /** @private */
6321     this.el = Roo.get(els.el, true);
6322     this.inner = Roo.get(els.inner, true);
6323     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
6324     this.pnode = Roo.get(els.el.parentNode, true);
6325     this.el.on("mousedown", this.onTabMouseDown, this);
6326     this.el.on("click", this.onTabClick, this);
6327     /** @private */
6328     if(closable){
6329         var c = Roo.get(els.close, true);
6330         c.dom.title = this.closeText;
6331         c.addClassOnOver("close-over");
6332         c.on("click", this.closeClick, this);
6333      }
6334
6335     this.addEvents({
6336          /**
6337          * @event activate
6338          * Fires when this tab becomes the active tab.
6339          * @param {Roo.TabPanel} tabPanel The parent TabPanel
6340          * @param {Roo.TabPanelItem} this
6341          */
6342         "activate": true,
6343         /**
6344          * @event beforeclose
6345          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
6346          * @param {Roo.TabPanelItem} this
6347          * @param {Object} e Set cancel to true on this object to cancel the close.
6348          */
6349         "beforeclose": true,
6350         /**
6351          * @event close
6352          * Fires when this tab is closed.
6353          * @param {Roo.TabPanelItem} this
6354          */
6355          "close": true,
6356         /**
6357          * @event deactivate
6358          * Fires when this tab is no longer the active tab.
6359          * @param {Roo.TabPanel} tabPanel The parent TabPanel
6360          * @param {Roo.TabPanelItem} this
6361          */
6362          "deactivate" : true
6363     });
6364     this.hidden = false;
6365
6366     Roo.TabPanelItem.superclass.constructor.call(this);
6367 };
6368
6369 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
6370     purgeListeners : function(){
6371        Roo.util.Observable.prototype.purgeListeners.call(this);
6372        this.el.removeAllListeners();
6373     },
6374     /**
6375      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
6376      */
6377     show : function(){
6378         this.pnode.addClass("on");
6379         this.showAction();
6380         if(Roo.isOpera){
6381             this.tabPanel.stripWrap.repaint();
6382         }
6383         this.fireEvent("activate", this.tabPanel, this);
6384     },
6385
6386     /**
6387      * Returns true if this tab is the active tab.
6388      * @return {Boolean}
6389      */
6390     isActive : function(){
6391         return this.tabPanel.getActiveTab() == this;
6392     },
6393
6394     /**
6395      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
6396      */
6397     hide : function(){
6398         this.pnode.removeClass("on");
6399         this.hideAction();
6400         this.fireEvent("deactivate", this.tabPanel, this);
6401     },
6402
6403     hideAction : function(){
6404         this.bodyEl.hide();
6405         this.bodyEl.setStyle("position", "absolute");
6406         this.bodyEl.setLeft("-20000px");
6407         this.bodyEl.setTop("-20000px");
6408     },
6409
6410     showAction : function(){
6411         this.bodyEl.setStyle("position", "relative");
6412         this.bodyEl.setTop("");
6413         this.bodyEl.setLeft("");
6414         this.bodyEl.show();
6415     },
6416
6417     /**
6418      * Set the tooltip for the tab.
6419      * @param {String} tooltip The tab's tooltip
6420      */
6421     setTooltip : function(text){
6422         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
6423             this.textEl.dom.qtip = text;
6424             this.textEl.dom.removeAttribute('title');
6425         }else{
6426             this.textEl.dom.title = text;
6427         }
6428     },
6429
6430     onTabClick : function(e){
6431         e.preventDefault();
6432         this.tabPanel.activate(this.id);
6433     },
6434
6435     onTabMouseDown : function(e){
6436         e.preventDefault();
6437         this.tabPanel.activate(this.id);
6438     },
6439
6440     getWidth : function(){
6441         return this.inner.getWidth();
6442     },
6443
6444     setWidth : function(width){
6445         var iwidth = width - this.pnode.getPadding("lr");
6446         this.inner.setWidth(iwidth);
6447         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
6448         this.pnode.setWidth(width);
6449     },
6450
6451     /**
6452      * Show or hide the tab
6453      * @param {Boolean} hidden True to hide or false to show.
6454      */
6455     setHidden : function(hidden){
6456         this.hidden = hidden;
6457         this.pnode.setStyle("display", hidden ? "none" : "");
6458     },
6459
6460     /**
6461      * Returns true if this tab is "hidden"
6462      * @return {Boolean}
6463      */
6464     isHidden : function(){
6465         return this.hidden;
6466     },
6467
6468     /**
6469      * Returns the text for this tab
6470      * @return {String}
6471      */
6472     getText : function(){
6473         return this.text;
6474     },
6475
6476     autoSize : function(){
6477         //this.el.beginMeasure();
6478         this.textEl.setWidth(1);
6479         /*
6480          *  #2804 [new] Tabs in Roojs
6481          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
6482          */
6483         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
6484         //this.el.endMeasure();
6485     },
6486
6487     /**
6488      * Sets the text for the tab (Note: this also sets the tooltip text)
6489      * @param {String} text The tab's text and tooltip
6490      */
6491     setText : function(text){
6492         this.text = text;
6493         this.textEl.update(text);
6494         this.setTooltip(text);
6495         if(!this.tabPanel.resizeTabs){
6496             this.autoSize();
6497         }
6498     },
6499     /**
6500      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
6501      */
6502     activate : function(){
6503         this.tabPanel.activate(this.id);
6504     },
6505
6506     /**
6507      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
6508      */
6509     disable : function(){
6510         if(this.tabPanel.active != this){
6511             this.disabled = true;
6512             this.pnode.addClass("disabled");
6513         }
6514     },
6515
6516     /**
6517      * Enables this TabPanelItem if it was previously disabled.
6518      */
6519     enable : function(){
6520         this.disabled = false;
6521         this.pnode.removeClass("disabled");
6522     },
6523
6524     /**
6525      * Sets the content for this TabPanelItem.
6526      * @param {String} content The content
6527      * @param {Boolean} loadScripts true to look for and load scripts
6528      */
6529     setContent : function(content, loadScripts){
6530         this.bodyEl.update(content, loadScripts);
6531     },
6532
6533     /**
6534      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
6535      * @return {Roo.UpdateManager} The UpdateManager
6536      */
6537     getUpdateManager : function(){
6538         return this.bodyEl.getUpdateManager();
6539     },
6540
6541     /**
6542      * Set a URL to be used to load the content for this TabPanelItem.
6543      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
6544      * @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)
6545      * @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)
6546      * @return {Roo.UpdateManager} The UpdateManager
6547      */
6548     setUrl : function(url, params, loadOnce){
6549         if(this.refreshDelegate){
6550             this.un('activate', this.refreshDelegate);
6551         }
6552         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
6553         this.on("activate", this.refreshDelegate);
6554         return this.bodyEl.getUpdateManager();
6555     },
6556
6557     /** @private */
6558     _handleRefresh : function(url, params, loadOnce){
6559         if(!loadOnce || !this.loaded){
6560             var updater = this.bodyEl.getUpdateManager();
6561             updater.update(url, params, this._setLoaded.createDelegate(this));
6562         }
6563     },
6564
6565     /**
6566      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
6567      *   Will fail silently if the setUrl method has not been called.
6568      *   This does not activate the panel, just updates its content.
6569      */
6570     refresh : function(){
6571         if(this.refreshDelegate){
6572            this.loaded = false;
6573            this.refreshDelegate();
6574         }
6575     },
6576
6577     /** @private */
6578     _setLoaded : function(){
6579         this.loaded = true;
6580     },
6581
6582     /** @private */
6583     closeClick : function(e){
6584         var o = {};
6585         e.stopEvent();
6586         this.fireEvent("beforeclose", this, o);
6587         if(o.cancel !== true){
6588             this.tabPanel.removeTab(this.id);
6589         }
6590     },
6591     /**
6592      * The text displayed in the tooltip for the close icon.
6593      * @type String
6594      */
6595     closeText : "Close this tab"
6596 });
6597
6598 /** @private */
6599 Roo.TabPanel.prototype.createStrip = function(container){
6600     var strip = document.createElement("div");
6601     strip.className = "x-tabs-wrap";
6602     container.appendChild(strip);
6603     return strip;
6604 };
6605 /** @private */
6606 Roo.TabPanel.prototype.createStripList = function(strip){
6607     // div wrapper for retard IE
6608     // returns the "tr" element.
6609     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
6610         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
6611         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
6612     return strip.firstChild.firstChild.firstChild.firstChild;
6613 };
6614 /** @private */
6615 Roo.TabPanel.prototype.createBody = function(container){
6616     var body = document.createElement("div");
6617     Roo.id(body, "tab-body");
6618     Roo.fly(body).addClass("x-tabs-body");
6619     container.appendChild(body);
6620     return body;
6621 };
6622 /** @private */
6623 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
6624     var body = Roo.getDom(id);
6625     if(!body){
6626         body = document.createElement("div");
6627         body.id = id;
6628     }
6629     Roo.fly(body).addClass("x-tabs-item-body");
6630     bodyEl.insertBefore(body, bodyEl.firstChild);
6631     return body;
6632 };
6633 /** @private */
6634 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
6635     var td = document.createElement("td");
6636     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
6637     //stripEl.appendChild(td);
6638     if(closable){
6639         td.className = "x-tabs-closable";
6640         if(!this.closeTpl){
6641             this.closeTpl = new Roo.Template(
6642                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6643                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
6644                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
6645             );
6646         }
6647         var el = this.closeTpl.overwrite(td, {"text": text});
6648         var close = el.getElementsByTagName("div")[0];
6649         var inner = el.getElementsByTagName("em")[0];
6650         return {"el": el, "close": close, "inner": inner};
6651     } else {
6652         if(!this.tabTpl){
6653             this.tabTpl = new Roo.Template(
6654                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6655                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
6656             );
6657         }
6658         var el = this.tabTpl.overwrite(td, {"text": text});
6659         var inner = el.getElementsByTagName("em")[0];
6660         return {"el": el, "inner": inner};
6661     }
6662 };/*
6663  * Based on:
6664  * Ext JS Library 1.1.1
6665  * Copyright(c) 2006-2007, Ext JS, LLC.
6666  *
6667  * Originally Released Under LGPL - original licence link has changed is not relivant.
6668  *
6669  * Fork - LGPL
6670  * <script type="text/javascript">
6671  */
6672
6673 /**
6674  * @class Roo.Button
6675  * @extends Roo.util.Observable
6676  * Simple Button class
6677  * @cfg {String} text The button text
6678  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
6679  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
6680  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
6681  * @cfg {Object} scope The scope of the handler
6682  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
6683  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
6684  * @cfg {Boolean} hidden True to start hidden (defaults to false)
6685  * @cfg {Boolean} disabled True to start disabled (defaults to false)
6686  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
6687  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
6688    applies if enableToggle = true)
6689  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
6690  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
6691   an {@link Roo.util.ClickRepeater} config object (defaults to false).
6692  * @constructor
6693  * Create a new button
6694  * @param {Object} config The config object
6695  */
6696 Roo.Button = function(renderTo, config)
6697 {
6698     if (!config) {
6699         config = renderTo;
6700         renderTo = config.renderTo || false;
6701     }
6702     
6703     Roo.apply(this, config);
6704     this.addEvents({
6705         /**
6706              * @event click
6707              * Fires when this button is clicked
6708              * @param {Button} this
6709              * @param {EventObject} e The click event
6710              */
6711             "click" : true,
6712         /**
6713              * @event toggle
6714              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
6715              * @param {Button} this
6716              * @param {Boolean} pressed
6717              */
6718             "toggle" : true,
6719         /**
6720              * @event mouseover
6721              * Fires when the mouse hovers over the button
6722              * @param {Button} this
6723              * @param {Event} e The event object
6724              */
6725         'mouseover' : true,
6726         /**
6727              * @event mouseout
6728              * Fires when the mouse exits the button
6729              * @param {Button} this
6730              * @param {Event} e The event object
6731              */
6732         'mouseout': true,
6733          /**
6734              * @event render
6735              * Fires when the button is rendered
6736              * @param {Button} this
6737              */
6738         'render': true
6739     });
6740     if(this.menu){
6741         this.menu = Roo.menu.MenuMgr.get(this.menu);
6742     }
6743     // register listeners first!!  - so render can be captured..
6744     Roo.util.Observable.call(this);
6745     if(renderTo){
6746         this.render(renderTo);
6747     }
6748     
6749   
6750 };
6751
6752 Roo.extend(Roo.Button, Roo.util.Observable, {
6753     /**
6754      * 
6755      */
6756     
6757     /**
6758      * Read-only. True if this button is hidden
6759      * @type Boolean
6760      */
6761     hidden : false,
6762     /**
6763      * Read-only. True if this button is disabled
6764      * @type Boolean
6765      */
6766     disabled : false,
6767     /**
6768      * Read-only. True if this button is pressed (only if enableToggle = true)
6769      * @type Boolean
6770      */
6771     pressed : false,
6772
6773     /**
6774      * @cfg {Number} tabIndex 
6775      * The DOM tabIndex for this button (defaults to undefined)
6776      */
6777     tabIndex : undefined,
6778
6779     /**
6780      * @cfg {Boolean} enableToggle
6781      * True to enable pressed/not pressed toggling (defaults to false)
6782      */
6783     enableToggle: false,
6784     /**
6785      * @cfg {Mixed} menu
6786      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
6787      */
6788     menu : undefined,
6789     /**
6790      * @cfg {String} menuAlign
6791      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
6792      */
6793     menuAlign : "tl-bl?",
6794
6795     /**
6796      * @cfg {String} iconCls
6797      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
6798      */
6799     iconCls : undefined,
6800     /**
6801      * @cfg {String} type
6802      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
6803      */
6804     type : 'button',
6805
6806     // private
6807     menuClassTarget: 'tr',
6808
6809     /**
6810      * @cfg {String} clickEvent
6811      * The type of event to map to the button's event handler (defaults to 'click')
6812      */
6813     clickEvent : 'click',
6814
6815     /**
6816      * @cfg {Boolean} handleMouseEvents
6817      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
6818      */
6819     handleMouseEvents : true,
6820
6821     /**
6822      * @cfg {String} tooltipType
6823      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
6824      */
6825     tooltipType : 'qtip',
6826
6827     /**
6828      * @cfg {String} cls
6829      * A CSS class to apply to the button's main element.
6830      */
6831     
6832     /**
6833      * @cfg {Roo.Template} template (Optional)
6834      * An {@link Roo.Template} with which to create the Button's main element. This Template must
6835      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
6836      * require code modifications if required elements (e.g. a button) aren't present.
6837      */
6838
6839     // private
6840     render : function(renderTo){
6841         var btn;
6842         if(this.hideParent){
6843             this.parentEl = Roo.get(renderTo);
6844         }
6845         if(!this.dhconfig){
6846             if(!this.template){
6847                 if(!Roo.Button.buttonTemplate){
6848                     // hideous table template
6849                     Roo.Button.buttonTemplate = new Roo.Template(
6850                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
6851                         '<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>',
6852                         "</tr></tbody></table>");
6853                 }
6854                 this.template = Roo.Button.buttonTemplate;
6855             }
6856             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
6857             var btnEl = btn.child("button:first");
6858             btnEl.on('focus', this.onFocus, this);
6859             btnEl.on('blur', this.onBlur, this);
6860             if(this.cls){
6861                 btn.addClass(this.cls);
6862             }
6863             if(this.icon){
6864                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
6865             }
6866             if(this.iconCls){
6867                 btnEl.addClass(this.iconCls);
6868                 if(!this.cls){
6869                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
6870                 }
6871             }
6872             if(this.tabIndex !== undefined){
6873                 btnEl.dom.tabIndex = this.tabIndex;
6874             }
6875             if(this.tooltip){
6876                 if(typeof this.tooltip == 'object'){
6877                     Roo.QuickTips.tips(Roo.apply({
6878                           target: btnEl.id
6879                     }, this.tooltip));
6880                 } else {
6881                     btnEl.dom[this.tooltipType] = this.tooltip;
6882                 }
6883             }
6884         }else{
6885             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
6886         }
6887         this.el = btn;
6888         if(this.id){
6889             this.el.dom.id = this.el.id = this.id;
6890         }
6891         if(this.menu){
6892             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
6893             this.menu.on("show", this.onMenuShow, this);
6894             this.menu.on("hide", this.onMenuHide, this);
6895         }
6896         btn.addClass("x-btn");
6897         if(Roo.isIE && !Roo.isIE7){
6898             this.autoWidth.defer(1, this);
6899         }else{
6900             this.autoWidth();
6901         }
6902         if(this.handleMouseEvents){
6903             btn.on("mouseover", this.onMouseOver, this);
6904             btn.on("mouseout", this.onMouseOut, this);
6905             btn.on("mousedown", this.onMouseDown, this);
6906         }
6907         btn.on(this.clickEvent, this.onClick, this);
6908         //btn.on("mouseup", this.onMouseUp, this);
6909         if(this.hidden){
6910             this.hide();
6911         }
6912         if(this.disabled){
6913             this.disable();
6914         }
6915         Roo.ButtonToggleMgr.register(this);
6916         if(this.pressed){
6917             this.el.addClass("x-btn-pressed");
6918         }
6919         if(this.repeat){
6920             var repeater = new Roo.util.ClickRepeater(btn,
6921                 typeof this.repeat == "object" ? this.repeat : {}
6922             );
6923             repeater.on("click", this.onClick,  this);
6924         }
6925         
6926         this.fireEvent('render', this);
6927         
6928     },
6929     /**
6930      * Returns the button's underlying element
6931      * @return {Roo.Element} The element
6932      */
6933     getEl : function(){
6934         return this.el;  
6935     },
6936     
6937     /**
6938      * Destroys this Button and removes any listeners.
6939      */
6940     destroy : function(){
6941         Roo.ButtonToggleMgr.unregister(this);
6942         this.el.removeAllListeners();
6943         this.purgeListeners();
6944         this.el.remove();
6945     },
6946
6947     // private
6948     autoWidth : function(){
6949         if(this.el){
6950             this.el.setWidth("auto");
6951             if(Roo.isIE7 && Roo.isStrict){
6952                 var ib = this.el.child('button');
6953                 if(ib && ib.getWidth() > 20){
6954                     ib.clip();
6955                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
6956                 }
6957             }
6958             if(this.minWidth){
6959                 if(this.hidden){
6960                     this.el.beginMeasure();
6961                 }
6962                 if(this.el.getWidth() < this.minWidth){
6963                     this.el.setWidth(this.minWidth);
6964                 }
6965                 if(this.hidden){
6966                     this.el.endMeasure();
6967                 }
6968             }
6969         }
6970     },
6971
6972     /**
6973      * Assigns this button's click handler
6974      * @param {Function} handler The function to call when the button is clicked
6975      * @param {Object} scope (optional) Scope for the function passed in
6976      */
6977     setHandler : function(handler, scope){
6978         this.handler = handler;
6979         this.scope = scope;  
6980     },
6981     
6982     /**
6983      * Sets this button's text
6984      * @param {String} text The button text
6985      */
6986     setText : function(text){
6987         this.text = text;
6988         if(this.el){
6989             this.el.child("td.x-btn-center button.x-btn-text").update(text);
6990         }
6991         this.autoWidth();
6992     },
6993     
6994     /**
6995      * Gets the text for this button
6996      * @return {String} The button text
6997      */
6998     getText : function(){
6999         return this.text;  
7000     },
7001     
7002     /**
7003      * Show this button
7004      */
7005     show: function(){
7006         this.hidden = false;
7007         if(this.el){
7008             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
7009         }
7010     },
7011     
7012     /**
7013      * Hide this button
7014      */
7015     hide: function(){
7016         this.hidden = true;
7017         if(this.el){
7018             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
7019         }
7020     },
7021     
7022     /**
7023      * Convenience function for boolean show/hide
7024      * @param {Boolean} visible True to show, false to hide
7025      */
7026     setVisible: function(visible){
7027         if(visible) {
7028             this.show();
7029         }else{
7030             this.hide();
7031         }
7032     },
7033     
7034     /**
7035      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
7036      * @param {Boolean} state (optional) Force a particular state
7037      */
7038     toggle : function(state){
7039         state = state === undefined ? !this.pressed : state;
7040         if(state != this.pressed){
7041             if(state){
7042                 this.el.addClass("x-btn-pressed");
7043                 this.pressed = true;
7044                 this.fireEvent("toggle", this, true);
7045             }else{
7046                 this.el.removeClass("x-btn-pressed");
7047                 this.pressed = false;
7048                 this.fireEvent("toggle", this, false);
7049             }
7050             if(this.toggleHandler){
7051                 this.toggleHandler.call(this.scope || this, this, state);
7052             }
7053         }
7054     },
7055     
7056     /**
7057      * Focus the button
7058      */
7059     focus : function(){
7060         this.el.child('button:first').focus();
7061     },
7062     
7063     /**
7064      * Disable this button
7065      */
7066     disable : function(){
7067         if(this.el){
7068             this.el.addClass("x-btn-disabled");
7069         }
7070         this.disabled = true;
7071     },
7072     
7073     /**
7074      * Enable this button
7075      */
7076     enable : function(){
7077         if(this.el){
7078             this.el.removeClass("x-btn-disabled");
7079         }
7080         this.disabled = false;
7081     },
7082
7083     /**
7084      * Convenience function for boolean enable/disable
7085      * @param {Boolean} enabled True to enable, false to disable
7086      */
7087     setDisabled : function(v){
7088         this[v !== true ? "enable" : "disable"]();
7089     },
7090
7091     // private
7092     onClick : function(e)
7093     {
7094         if(e){
7095             e.preventDefault();
7096         }
7097         if(e.button != 0){
7098             return;
7099         }
7100         if(!this.disabled){
7101             if(this.enableToggle){
7102                 this.toggle();
7103             }
7104             if(this.menu && !this.menu.isVisible()){
7105                 this.menu.show(this.el, this.menuAlign);
7106             }
7107             this.fireEvent("click", this, e);
7108             if(this.handler){
7109                 this.el.removeClass("x-btn-over");
7110                 this.handler.call(this.scope || this, this, e);
7111             }
7112         }
7113     },
7114     // private
7115     onMouseOver : function(e){
7116         if(!this.disabled){
7117             this.el.addClass("x-btn-over");
7118             this.fireEvent('mouseover', this, e);
7119         }
7120     },
7121     // private
7122     onMouseOut : function(e){
7123         if(!e.within(this.el,  true)){
7124             this.el.removeClass("x-btn-over");
7125             this.fireEvent('mouseout', this, e);
7126         }
7127     },
7128     // private
7129     onFocus : function(e){
7130         if(!this.disabled){
7131             this.el.addClass("x-btn-focus");
7132         }
7133     },
7134     // private
7135     onBlur : function(e){
7136         this.el.removeClass("x-btn-focus");
7137     },
7138     // private
7139     onMouseDown : function(e){
7140         if(!this.disabled && e.button == 0){
7141             this.el.addClass("x-btn-click");
7142             Roo.get(document).on('mouseup', this.onMouseUp, this);
7143         }
7144     },
7145     // private
7146     onMouseUp : function(e){
7147         if(e.button == 0){
7148             this.el.removeClass("x-btn-click");
7149             Roo.get(document).un('mouseup', this.onMouseUp, this);
7150         }
7151     },
7152     // private
7153     onMenuShow : function(e){
7154         this.el.addClass("x-btn-menu-active");
7155     },
7156     // private
7157     onMenuHide : function(e){
7158         this.el.removeClass("x-btn-menu-active");
7159     }   
7160 });
7161
7162 // Private utility class used by Button
7163 Roo.ButtonToggleMgr = function(){
7164    var groups = {};
7165    
7166    function toggleGroup(btn, state){
7167        if(state){
7168            var g = groups[btn.toggleGroup];
7169            for(var i = 0, l = g.length; i < l; i++){
7170                if(g[i] != btn){
7171                    g[i].toggle(false);
7172                }
7173            }
7174        }
7175    }
7176    
7177    return {
7178        register : function(btn){
7179            if(!btn.toggleGroup){
7180                return;
7181            }
7182            var g = groups[btn.toggleGroup];
7183            if(!g){
7184                g = groups[btn.toggleGroup] = [];
7185            }
7186            g.push(btn);
7187            btn.on("toggle", toggleGroup);
7188        },
7189        
7190        unregister : function(btn){
7191            if(!btn.toggleGroup){
7192                return;
7193            }
7194            var g = groups[btn.toggleGroup];
7195            if(g){
7196                g.remove(btn);
7197                btn.un("toggle", toggleGroup);
7198            }
7199        }
7200    };
7201 }();/*
7202  * Based on:
7203  * Ext JS Library 1.1.1
7204  * Copyright(c) 2006-2007, Ext JS, LLC.
7205  *
7206  * Originally Released Under LGPL - original licence link has changed is not relivant.
7207  *
7208  * Fork - LGPL
7209  * <script type="text/javascript">
7210  */
7211  
7212 /**
7213  * @class Roo.SplitButton
7214  * @extends Roo.Button
7215  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
7216  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
7217  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
7218  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
7219  * @cfg {String} arrowTooltip The title attribute of the arrow
7220  * @constructor
7221  * Create a new menu button
7222  * @param {String/HTMLElement/Element} renderTo The element to append the button to
7223  * @param {Object} config The config object
7224  */
7225 Roo.SplitButton = function(renderTo, config){
7226     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
7227     /**
7228      * @event arrowclick
7229      * Fires when this button's arrow is clicked
7230      * @param {SplitButton} this
7231      * @param {EventObject} e The click event
7232      */
7233     this.addEvents({"arrowclick":true});
7234 };
7235
7236 Roo.extend(Roo.SplitButton, Roo.Button, {
7237     render : function(renderTo){
7238         // this is one sweet looking template!
7239         var tpl = new Roo.Template(
7240             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
7241             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
7242             '<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>',
7243             "</tbody></table></td><td>",
7244             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
7245             '<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>',
7246             "</tbody></table></td></tr></table>"
7247         );
7248         var btn = tpl.append(renderTo, [this.text, this.type], true);
7249         var btnEl = btn.child("button");
7250         if(this.cls){
7251             btn.addClass(this.cls);
7252         }
7253         if(this.icon){
7254             btnEl.setStyle('background-image', 'url(' +this.icon +')');
7255         }
7256         if(this.iconCls){
7257             btnEl.addClass(this.iconCls);
7258             if(!this.cls){
7259                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
7260             }
7261         }
7262         this.el = btn;
7263         if(this.handleMouseEvents){
7264             btn.on("mouseover", this.onMouseOver, this);
7265             btn.on("mouseout", this.onMouseOut, this);
7266             btn.on("mousedown", this.onMouseDown, this);
7267             btn.on("mouseup", this.onMouseUp, this);
7268         }
7269         btn.on(this.clickEvent, this.onClick, this);
7270         if(this.tooltip){
7271             if(typeof this.tooltip == 'object'){
7272                 Roo.QuickTips.tips(Roo.apply({
7273                       target: btnEl.id
7274                 }, this.tooltip));
7275             } else {
7276                 btnEl.dom[this.tooltipType] = this.tooltip;
7277             }
7278         }
7279         if(this.arrowTooltip){
7280             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
7281         }
7282         if(this.hidden){
7283             this.hide();
7284         }
7285         if(this.disabled){
7286             this.disable();
7287         }
7288         if(this.pressed){
7289             this.el.addClass("x-btn-pressed");
7290         }
7291         if(Roo.isIE && !Roo.isIE7){
7292             this.autoWidth.defer(1, this);
7293         }else{
7294             this.autoWidth();
7295         }
7296         if(this.menu){
7297             this.menu.on("show", this.onMenuShow, this);
7298             this.menu.on("hide", this.onMenuHide, this);
7299         }
7300         this.fireEvent('render', this);
7301     },
7302
7303     // private
7304     autoWidth : function(){
7305         if(this.el){
7306             var tbl = this.el.child("table:first");
7307             var tbl2 = this.el.child("table:last");
7308             this.el.setWidth("auto");
7309             tbl.setWidth("auto");
7310             if(Roo.isIE7 && Roo.isStrict){
7311                 var ib = this.el.child('button:first');
7312                 if(ib && ib.getWidth() > 20){
7313                     ib.clip();
7314                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
7315                 }
7316             }
7317             if(this.minWidth){
7318                 if(this.hidden){
7319                     this.el.beginMeasure();
7320                 }
7321                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
7322                     tbl.setWidth(this.minWidth-tbl2.getWidth());
7323                 }
7324                 if(this.hidden){
7325                     this.el.endMeasure();
7326                 }
7327             }
7328             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
7329         } 
7330     },
7331     /**
7332      * Sets this button's click handler
7333      * @param {Function} handler The function to call when the button is clicked
7334      * @param {Object} scope (optional) Scope for the function passed above
7335      */
7336     setHandler : function(handler, scope){
7337         this.handler = handler;
7338         this.scope = scope;  
7339     },
7340     
7341     /**
7342      * Sets this button's arrow click handler
7343      * @param {Function} handler The function to call when the arrow is clicked
7344      * @param {Object} scope (optional) Scope for the function passed above
7345      */
7346     setArrowHandler : function(handler, scope){
7347         this.arrowHandler = handler;
7348         this.scope = scope;  
7349     },
7350     
7351     /**
7352      * Focus the button
7353      */
7354     focus : function(){
7355         if(this.el){
7356             this.el.child("button:first").focus();
7357         }
7358     },
7359
7360     // private
7361     onClick : function(e){
7362         e.preventDefault();
7363         if(!this.disabled){
7364             if(e.getTarget(".x-btn-menu-arrow-wrap")){
7365                 if(this.menu && !this.menu.isVisible()){
7366                     this.menu.show(this.el, this.menuAlign);
7367                 }
7368                 this.fireEvent("arrowclick", this, e);
7369                 if(this.arrowHandler){
7370                     this.arrowHandler.call(this.scope || this, this, e);
7371                 }
7372             }else{
7373                 this.fireEvent("click", this, e);
7374                 if(this.handler){
7375                     this.handler.call(this.scope || this, this, e);
7376                 }
7377             }
7378         }
7379     },
7380     // private
7381     onMouseDown : function(e){
7382         if(!this.disabled){
7383             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
7384         }
7385     },
7386     // private
7387     onMouseUp : function(e){
7388         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
7389     }   
7390 });
7391
7392
7393 // backwards compat
7394 Roo.MenuButton = Roo.SplitButton;/*
7395  * Based on:
7396  * Ext JS Library 1.1.1
7397  * Copyright(c) 2006-2007, Ext JS, LLC.
7398  *
7399  * Originally Released Under LGPL - original licence link has changed is not relivant.
7400  *
7401  * Fork - LGPL
7402  * <script type="text/javascript">
7403  */
7404
7405 /**
7406  * @class Roo.Toolbar
7407  * Basic Toolbar class.
7408  * @constructor
7409  * Creates a new Toolbar
7410  * @param {Object} container The config object
7411  */ 
7412 Roo.Toolbar = function(container, buttons, config)
7413 {
7414     /// old consturctor format still supported..
7415     if(container instanceof Array){ // omit the container for later rendering
7416         buttons = container;
7417         config = buttons;
7418         container = null;
7419     }
7420     if (typeof(container) == 'object' && container.xtype) {
7421         config = container;
7422         container = config.container;
7423         buttons = config.buttons || []; // not really - use items!!
7424     }
7425     var xitems = [];
7426     if (config && config.items) {
7427         xitems = config.items;
7428         delete config.items;
7429     }
7430     Roo.apply(this, config);
7431     this.buttons = buttons;
7432     
7433     if(container){
7434         this.render(container);
7435     }
7436     this.xitems = xitems;
7437     Roo.each(xitems, function(b) {
7438         this.add(b);
7439     }, this);
7440     
7441 };
7442
7443 Roo.Toolbar.prototype = {
7444     /**
7445      * @cfg {Array} items
7446      * array of button configs or elements to add (will be converted to a MixedCollection)
7447      */
7448     
7449     /**
7450      * @cfg {String/HTMLElement/Element} container
7451      * The id or element that will contain the toolbar
7452      */
7453     // private
7454     render : function(ct){
7455         this.el = Roo.get(ct);
7456         if(this.cls){
7457             this.el.addClass(this.cls);
7458         }
7459         // using a table allows for vertical alignment
7460         // 100% width is needed by Safari...
7461         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
7462         this.tr = this.el.child("tr", true);
7463         var autoId = 0;
7464         this.items = new Roo.util.MixedCollection(false, function(o){
7465             return o.id || ("item" + (++autoId));
7466         });
7467         if(this.buttons){
7468             this.add.apply(this, this.buttons);
7469             delete this.buttons;
7470         }
7471     },
7472
7473     /**
7474      * Adds element(s) to the toolbar -- this function takes a variable number of 
7475      * arguments of mixed type and adds them to the toolbar.
7476      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
7477      * <ul>
7478      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
7479      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
7480      * <li>Field: Any form field (equivalent to {@link #addField})</li>
7481      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
7482      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
7483      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
7484      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
7485      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
7486      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
7487      * </ul>
7488      * @param {Mixed} arg2
7489      * @param {Mixed} etc.
7490      */
7491     add : function(){
7492         var a = arguments, l = a.length;
7493         for(var i = 0; i < l; i++){
7494             this._add(a[i]);
7495         }
7496     },
7497     // private..
7498     _add : function(el) {
7499         
7500         if (el.xtype) {
7501             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
7502         }
7503         
7504         if (el.applyTo){ // some kind of form field
7505             return this.addField(el);
7506         } 
7507         if (el.render){ // some kind of Toolbar.Item
7508             return this.addItem(el);
7509         }
7510         if (typeof el == "string"){ // string
7511             if(el == "separator" || el == "-"){
7512                 return this.addSeparator();
7513             }
7514             if (el == " "){
7515                 return this.addSpacer();
7516             }
7517             if(el == "->"){
7518                 return this.addFill();
7519             }
7520             return this.addText(el);
7521             
7522         }
7523         if(el.tagName){ // element
7524             return this.addElement(el);
7525         }
7526         if(typeof el == "object"){ // must be button config?
7527             return this.addButton(el);
7528         }
7529         // and now what?!?!
7530         return false;
7531         
7532     },
7533     
7534     /**
7535      * Add an Xtype element
7536      * @param {Object} xtype Xtype Object
7537      * @return {Object} created Object
7538      */
7539     addxtype : function(e){
7540         return this.add(e);  
7541     },
7542     
7543     /**
7544      * Returns the Element for this toolbar.
7545      * @return {Roo.Element}
7546      */
7547     getEl : function(){
7548         return this.el;  
7549     },
7550     
7551     /**
7552      * Adds a separator
7553      * @return {Roo.Toolbar.Item} The separator item
7554      */
7555     addSeparator : function(){
7556         return this.addItem(new Roo.Toolbar.Separator());
7557     },
7558
7559     /**
7560      * Adds a spacer element
7561      * @return {Roo.Toolbar.Spacer} The spacer item
7562      */
7563     addSpacer : function(){
7564         return this.addItem(new Roo.Toolbar.Spacer());
7565     },
7566
7567     /**
7568      * Adds a fill element that forces subsequent additions to the right side of the toolbar
7569      * @return {Roo.Toolbar.Fill} The fill item
7570      */
7571     addFill : function(){
7572         return this.addItem(new Roo.Toolbar.Fill());
7573     },
7574
7575     /**
7576      * Adds any standard HTML element to the toolbar
7577      * @param {String/HTMLElement/Element} el The element or id of the element to add
7578      * @return {Roo.Toolbar.Item} The element's item
7579      */
7580     addElement : function(el){
7581         return this.addItem(new Roo.Toolbar.Item(el));
7582     },
7583     /**
7584      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
7585      * @type Roo.util.MixedCollection  
7586      */
7587     items : false,
7588      
7589     /**
7590      * Adds any Toolbar.Item or subclass
7591      * @param {Roo.Toolbar.Item} item
7592      * @return {Roo.Toolbar.Item} The item
7593      */
7594     addItem : function(item){
7595         var td = this.nextBlock();
7596         item.render(td);
7597         this.items.add(item);
7598         return item;
7599     },
7600     
7601     /**
7602      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
7603      * @param {Object/Array} config A button config or array of configs
7604      * @return {Roo.Toolbar.Button/Array}
7605      */
7606     addButton : function(config){
7607         if(config instanceof Array){
7608             var buttons = [];
7609             for(var i = 0, len = config.length; i < len; i++) {
7610                 buttons.push(this.addButton(config[i]));
7611             }
7612             return buttons;
7613         }
7614         var b = config;
7615         if(!(config instanceof Roo.Toolbar.Button)){
7616             b = config.split ?
7617                 new Roo.Toolbar.SplitButton(config) :
7618                 new Roo.Toolbar.Button(config);
7619         }
7620         var td = this.nextBlock();
7621         b.render(td);
7622         this.items.add(b);
7623         return b;
7624     },
7625     
7626     /**
7627      * Adds text to the toolbar
7628      * @param {String} text The text to add
7629      * @return {Roo.Toolbar.Item} The element's item
7630      */
7631     addText : function(text){
7632         return this.addItem(new Roo.Toolbar.TextItem(text));
7633     },
7634     
7635     /**
7636      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
7637      * @param {Number} index The index where the item is to be inserted
7638      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
7639      * @return {Roo.Toolbar.Button/Item}
7640      */
7641     insertButton : function(index, item){
7642         if(item instanceof Array){
7643             var buttons = [];
7644             for(var i = 0, len = item.length; i < len; i++) {
7645                buttons.push(this.insertButton(index + i, item[i]));
7646             }
7647             return buttons;
7648         }
7649         if (!(item instanceof Roo.Toolbar.Button)){
7650            item = new Roo.Toolbar.Button(item);
7651         }
7652         var td = document.createElement("td");
7653         this.tr.insertBefore(td, this.tr.childNodes[index]);
7654         item.render(td);
7655         this.items.insert(index, item);
7656         return item;
7657     },
7658     
7659     /**
7660      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
7661      * @param {Object} config
7662      * @return {Roo.Toolbar.Item} The element's item
7663      */
7664     addDom : function(config, returnEl){
7665         var td = this.nextBlock();
7666         Roo.DomHelper.overwrite(td, config);
7667         var ti = new Roo.Toolbar.Item(td.firstChild);
7668         ti.render(td);
7669         this.items.add(ti);
7670         return ti;
7671     },
7672
7673     /**
7674      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
7675      * @type Roo.util.MixedCollection  
7676      */
7677     fields : false,
7678     
7679     /**
7680      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
7681      * Note: the field should not have been rendered yet. For a field that has already been
7682      * rendered, use {@link #addElement}.
7683      * @param {Roo.form.Field} field
7684      * @return {Roo.ToolbarItem}
7685      */
7686      
7687       
7688     addField : function(field) {
7689         if (!this.fields) {
7690             var autoId = 0;
7691             this.fields = new Roo.util.MixedCollection(false, function(o){
7692                 return o.id || ("item" + (++autoId));
7693             });
7694
7695         }
7696         
7697         var td = this.nextBlock();
7698         field.render(td);
7699         var ti = new Roo.Toolbar.Item(td.firstChild);
7700         ti.render(td);
7701         this.items.add(ti);
7702         this.fields.add(field);
7703         return ti;
7704     },
7705     /**
7706      * Hide the toolbar
7707      * @method hide
7708      */
7709      
7710       
7711     hide : function()
7712     {
7713         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
7714         this.el.child('div').hide();
7715     },
7716     /**
7717      * Show the toolbar
7718      * @method show
7719      */
7720     show : function()
7721     {
7722         this.el.child('div').show();
7723     },
7724       
7725     // private
7726     nextBlock : function(){
7727         var td = document.createElement("td");
7728         this.tr.appendChild(td);
7729         return td;
7730     },
7731
7732     // private
7733     destroy : function(){
7734         if(this.items){ // rendered?
7735             Roo.destroy.apply(Roo, this.items.items);
7736         }
7737         if(this.fields){ // rendered?
7738             Roo.destroy.apply(Roo, this.fields.items);
7739         }
7740         Roo.Element.uncache(this.el, this.tr);
7741     }
7742 };
7743
7744 /**
7745  * @class Roo.Toolbar.Item
7746  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
7747  * @constructor
7748  * Creates a new Item
7749  * @param {HTMLElement} el 
7750  */
7751 Roo.Toolbar.Item = function(el){
7752     var cfg = {};
7753     if (typeof (el.xtype) != 'undefined') {
7754         cfg = el;
7755         el = cfg.el;
7756     }
7757     
7758     this.el = Roo.getDom(el);
7759     this.id = Roo.id(this.el);
7760     this.hidden = false;
7761     
7762     this.addEvents({
7763          /**
7764              * @event render
7765              * Fires when the button is rendered
7766              * @param {Button} this
7767              */
7768         'render': true
7769     });
7770     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
7771 };
7772 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
7773 //Roo.Toolbar.Item.prototype = {
7774     
7775     /**
7776      * Get this item's HTML Element
7777      * @return {HTMLElement}
7778      */
7779     getEl : function(){
7780        return this.el;  
7781     },
7782
7783     // private
7784     render : function(td){
7785         
7786          this.td = td;
7787         td.appendChild(this.el);
7788         
7789         this.fireEvent('render', this);
7790     },
7791     
7792     /**
7793      * Removes and destroys this item.
7794      */
7795     destroy : function(){
7796         this.td.parentNode.removeChild(this.td);
7797     },
7798     
7799     /**
7800      * Shows this item.
7801      */
7802     show: function(){
7803         this.hidden = false;
7804         this.td.style.display = "";
7805     },
7806     
7807     /**
7808      * Hides this item.
7809      */
7810     hide: function(){
7811         this.hidden = true;
7812         this.td.style.display = "none";
7813     },
7814     
7815     /**
7816      * Convenience function for boolean show/hide.
7817      * @param {Boolean} visible true to show/false to hide
7818      */
7819     setVisible: function(visible){
7820         if(visible) {
7821             this.show();
7822         }else{
7823             this.hide();
7824         }
7825     },
7826     
7827     /**
7828      * Try to focus this item.
7829      */
7830     focus : function(){
7831         Roo.fly(this.el).focus();
7832     },
7833     
7834     /**
7835      * Disables this item.
7836      */
7837     disable : function(){
7838         Roo.fly(this.td).addClass("x-item-disabled");
7839         this.disabled = true;
7840         this.el.disabled = true;
7841     },
7842     
7843     /**
7844      * Enables this item.
7845      */
7846     enable : function(){
7847         Roo.fly(this.td).removeClass("x-item-disabled");
7848         this.disabled = false;
7849         this.el.disabled = false;
7850     }
7851 });
7852
7853
7854 /**
7855  * @class Roo.Toolbar.Separator
7856  * @extends Roo.Toolbar.Item
7857  * A simple toolbar separator class
7858  * @constructor
7859  * Creates a new Separator
7860  */
7861 Roo.Toolbar.Separator = function(cfg){
7862     
7863     var s = document.createElement("span");
7864     s.className = "ytb-sep";
7865     if (cfg) {
7866         cfg.el = s;
7867     }
7868     
7869     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
7870 };
7871 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
7872     enable:Roo.emptyFn,
7873     disable:Roo.emptyFn,
7874     focus:Roo.emptyFn
7875 });
7876
7877 /**
7878  * @class Roo.Toolbar.Spacer
7879  * @extends Roo.Toolbar.Item
7880  * A simple element that adds extra horizontal space to a toolbar.
7881  * @constructor
7882  * Creates a new Spacer
7883  */
7884 Roo.Toolbar.Spacer = function(cfg){
7885     var s = document.createElement("div");
7886     s.className = "ytb-spacer";
7887     if (cfg) {
7888         cfg.el = s;
7889     }
7890     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
7891 };
7892 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
7893     enable:Roo.emptyFn,
7894     disable:Roo.emptyFn,
7895     focus:Roo.emptyFn
7896 });
7897
7898 /**
7899  * @class Roo.Toolbar.Fill
7900  * @extends Roo.Toolbar.Spacer
7901  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
7902  * @constructor
7903  * Creates a new Spacer
7904  */
7905 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
7906     // private
7907     render : function(td){
7908         td.style.width = '100%';
7909         Roo.Toolbar.Fill.superclass.render.call(this, td);
7910     }
7911 });
7912
7913 /**
7914  * @class Roo.Toolbar.TextItem
7915  * @extends Roo.Toolbar.Item
7916  * A simple class that renders text directly into a toolbar.
7917  * @constructor
7918  * Creates a new TextItem
7919  * @param {String} text
7920  */
7921 Roo.Toolbar.TextItem = function(cfg){
7922     var  text = cfg || "";
7923     if (typeof(cfg) == 'object') {
7924         text = cfg.text || "";
7925     }  else {
7926         cfg = null;
7927     }
7928     var s = document.createElement("span");
7929     s.className = "ytb-text";
7930     s.innerHTML = text;
7931     if (cfg) {
7932         cfg.el  = s;
7933     }
7934     
7935     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
7936 };
7937 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
7938     
7939      
7940     enable:Roo.emptyFn,
7941     disable:Roo.emptyFn,
7942     focus:Roo.emptyFn
7943 });
7944
7945 /**
7946  * @class Roo.Toolbar.Button
7947  * @extends Roo.Button
7948  * A button that renders into a toolbar.
7949  * @constructor
7950  * Creates a new Button
7951  * @param {Object} config A standard {@link Roo.Button} config object
7952  */
7953 Roo.Toolbar.Button = function(config){
7954     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
7955 };
7956 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
7957     render : function(td){
7958         this.td = td;
7959         Roo.Toolbar.Button.superclass.render.call(this, td);
7960     },
7961     
7962     /**
7963      * Removes and destroys this button
7964      */
7965     destroy : function(){
7966         Roo.Toolbar.Button.superclass.destroy.call(this);
7967         this.td.parentNode.removeChild(this.td);
7968     },
7969     
7970     /**
7971      * Shows this button
7972      */
7973     show: function(){
7974         this.hidden = false;
7975         this.td.style.display = "";
7976     },
7977     
7978     /**
7979      * Hides this button
7980      */
7981     hide: function(){
7982         this.hidden = true;
7983         this.td.style.display = "none";
7984     },
7985
7986     /**
7987      * Disables this item
7988      */
7989     disable : function(){
7990         Roo.fly(this.td).addClass("x-item-disabled");
7991         this.disabled = true;
7992     },
7993
7994     /**
7995      * Enables this item
7996      */
7997     enable : function(){
7998         Roo.fly(this.td).removeClass("x-item-disabled");
7999         this.disabled = false;
8000     }
8001 });
8002 // backwards compat
8003 Roo.ToolbarButton = Roo.Toolbar.Button;
8004
8005 /**
8006  * @class Roo.Toolbar.SplitButton
8007  * @extends Roo.SplitButton
8008  * A menu button that renders into a toolbar.
8009  * @constructor
8010  * Creates a new SplitButton
8011  * @param {Object} config A standard {@link Roo.SplitButton} config object
8012  */
8013 Roo.Toolbar.SplitButton = function(config){
8014     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
8015 };
8016 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
8017     render : function(td){
8018         this.td = td;
8019         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
8020     },
8021     
8022     /**
8023      * Removes and destroys this button
8024      */
8025     destroy : function(){
8026         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
8027         this.td.parentNode.removeChild(this.td);
8028     },
8029     
8030     /**
8031      * Shows this button
8032      */
8033     show: function(){
8034         this.hidden = false;
8035         this.td.style.display = "";
8036     },
8037     
8038     /**
8039      * Hides this button
8040      */
8041     hide: function(){
8042         this.hidden = true;
8043         this.td.style.display = "none";
8044     }
8045 });
8046
8047 // backwards compat
8048 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
8049  * Based on:
8050  * Ext JS Library 1.1.1
8051  * Copyright(c) 2006-2007, Ext JS, LLC.
8052  *
8053  * Originally Released Under LGPL - original licence link has changed is not relivant.
8054  *
8055  * Fork - LGPL
8056  * <script type="text/javascript">
8057  */
8058  
8059 /**
8060  * @class Roo.PagingToolbar
8061  * @extends Roo.Toolbar
8062  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
8063  * @constructor
8064  * Create a new PagingToolbar
8065  * @param {Object} config The config object
8066  */
8067 Roo.PagingToolbar = function(el, ds, config)
8068 {
8069     // old args format still supported... - xtype is prefered..
8070     if (typeof(el) == 'object' && el.xtype) {
8071         // created from xtype...
8072         config = el;
8073         ds = el.dataSource;
8074         el = config.container;
8075     }
8076     var items = [];
8077     if (config.items) {
8078         items = config.items;
8079         config.items = [];
8080     }
8081     
8082     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
8083     this.ds = ds;
8084     this.cursor = 0;
8085     this.renderButtons(this.el);
8086     this.bind(ds);
8087     
8088     // supprot items array.
8089    
8090     Roo.each(items, function(e) {
8091         this.add(Roo.factory(e));
8092     },this);
8093     
8094 };
8095
8096 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
8097     /**
8098      * @cfg {Roo.data.Store} dataSource
8099      * The underlying data store providing the paged data
8100      */
8101     /**
8102      * @cfg {String/HTMLElement/Element} container
8103      * container The id or element that will contain the toolbar
8104      */
8105     /**
8106      * @cfg {Boolean} displayInfo
8107      * True to display the displayMsg (defaults to false)
8108      */
8109     /**
8110      * @cfg {Number} pageSize
8111      * The number of records to display per page (defaults to 20)
8112      */
8113     pageSize: 20,
8114     /**
8115      * @cfg {String} displayMsg
8116      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
8117      */
8118     displayMsg : 'Displaying {0} - {1} of {2}',
8119     /**
8120      * @cfg {String} emptyMsg
8121      * The message to display when no records are found (defaults to "No data to display")
8122      */
8123     emptyMsg : 'No data to display',
8124     /**
8125      * Customizable piece of the default paging text (defaults to "Page")
8126      * @type String
8127      */
8128     beforePageText : "Page",
8129     /**
8130      * Customizable piece of the default paging text (defaults to "of %0")
8131      * @type String
8132      */
8133     afterPageText : "of {0}",
8134     /**
8135      * Customizable piece of the default paging text (defaults to "First Page")
8136      * @type String
8137      */
8138     firstText : "First Page",
8139     /**
8140      * Customizable piece of the default paging text (defaults to "Previous Page")
8141      * @type String
8142      */
8143     prevText : "Previous Page",
8144     /**
8145      * Customizable piece of the default paging text (defaults to "Next Page")
8146      * @type String
8147      */
8148     nextText : "Next Page",
8149     /**
8150      * Customizable piece of the default paging text (defaults to "Last Page")
8151      * @type String
8152      */
8153     lastText : "Last Page",
8154     /**
8155      * Customizable piece of the default paging text (defaults to "Refresh")
8156      * @type String
8157      */
8158     refreshText : "Refresh",
8159
8160     // private
8161     renderButtons : function(el){
8162         Roo.PagingToolbar.superclass.render.call(this, el);
8163         this.first = this.addButton({
8164             tooltip: this.firstText,
8165             cls: "x-btn-icon x-grid-page-first",
8166             disabled: true,
8167             handler: this.onClick.createDelegate(this, ["first"])
8168         });
8169         this.prev = this.addButton({
8170             tooltip: this.prevText,
8171             cls: "x-btn-icon x-grid-page-prev",
8172             disabled: true,
8173             handler: this.onClick.createDelegate(this, ["prev"])
8174         });
8175         //this.addSeparator();
8176         this.add(this.beforePageText);
8177         this.field = Roo.get(this.addDom({
8178            tag: "input",
8179            type: "text",
8180            size: "3",
8181            value: "1",
8182            cls: "x-grid-page-number"
8183         }).el);
8184         this.field.on("keydown", this.onPagingKeydown, this);
8185         this.field.on("focus", function(){this.dom.select();});
8186         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
8187         this.field.setHeight(18);
8188         //this.addSeparator();
8189         this.next = this.addButton({
8190             tooltip: this.nextText,
8191             cls: "x-btn-icon x-grid-page-next",
8192             disabled: true,
8193             handler: this.onClick.createDelegate(this, ["next"])
8194         });
8195         this.last = this.addButton({
8196             tooltip: this.lastText,
8197             cls: "x-btn-icon x-grid-page-last",
8198             disabled: true,
8199             handler: this.onClick.createDelegate(this, ["last"])
8200         });
8201         //this.addSeparator();
8202         this.loading = this.addButton({
8203             tooltip: this.refreshText,
8204             cls: "x-btn-icon x-grid-loading",
8205             handler: this.onClick.createDelegate(this, ["refresh"])
8206         });
8207
8208         if(this.displayInfo){
8209             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
8210         }
8211     },
8212
8213     // private
8214     updateInfo : function(){
8215         if(this.displayEl){
8216             var count = this.ds.getCount();
8217             var msg = count == 0 ?
8218                 this.emptyMsg :
8219                 String.format(
8220                     this.displayMsg,
8221                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
8222                 );
8223             this.displayEl.update(msg);
8224         }
8225     },
8226
8227     // private
8228     onLoad : function(ds, r, o){
8229        this.cursor = o.params ? o.params.start : 0;
8230        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
8231
8232        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
8233        this.field.dom.value = ap;
8234        this.first.setDisabled(ap == 1);
8235        this.prev.setDisabled(ap == 1);
8236        this.next.setDisabled(ap == ps);
8237        this.last.setDisabled(ap == ps);
8238        this.loading.enable();
8239        this.updateInfo();
8240     },
8241
8242     // private
8243     getPageData : function(){
8244         var total = this.ds.getTotalCount();
8245         return {
8246             total : total,
8247             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
8248             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
8249         };
8250     },
8251
8252     // private
8253     onLoadError : function(){
8254         this.loading.enable();
8255     },
8256
8257     // private
8258     onPagingKeydown : function(e){
8259         var k = e.getKey();
8260         var d = this.getPageData();
8261         if(k == e.RETURN){
8262             var v = this.field.dom.value, pageNum;
8263             if(!v || isNaN(pageNum = parseInt(v, 10))){
8264                 this.field.dom.value = d.activePage;
8265                 return;
8266             }
8267             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
8268             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
8269             e.stopEvent();
8270         }
8271         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))
8272         {
8273           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
8274           this.field.dom.value = pageNum;
8275           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
8276           e.stopEvent();
8277         }
8278         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
8279         {
8280           var v = this.field.dom.value, pageNum; 
8281           var increment = (e.shiftKey) ? 10 : 1;
8282           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
8283             increment *= -1;
8284           }
8285           if(!v || isNaN(pageNum = parseInt(v, 10))) {
8286             this.field.dom.value = d.activePage;
8287             return;
8288           }
8289           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
8290           {
8291             this.field.dom.value = parseInt(v, 10) + increment;
8292             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
8293             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
8294           }
8295           e.stopEvent();
8296         }
8297     },
8298
8299     // private
8300     beforeLoad : function(){
8301         if(this.loading){
8302             this.loading.disable();
8303         }
8304     },
8305
8306     // private
8307     onClick : function(which){
8308         var ds = this.ds;
8309         switch(which){
8310             case "first":
8311                 ds.load({params:{start: 0, limit: this.pageSize}});
8312             break;
8313             case "prev":
8314                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
8315             break;
8316             case "next":
8317                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
8318             break;
8319             case "last":
8320                 var total = ds.getTotalCount();
8321                 var extra = total % this.pageSize;
8322                 var lastStart = extra ? (total - extra) : total-this.pageSize;
8323                 ds.load({params:{start: lastStart, limit: this.pageSize}});
8324             break;
8325             case "refresh":
8326                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
8327             break;
8328         }
8329     },
8330
8331     /**
8332      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
8333      * @param {Roo.data.Store} store The data store to unbind
8334      */
8335     unbind : function(ds){
8336         ds.un("beforeload", this.beforeLoad, this);
8337         ds.un("load", this.onLoad, this);
8338         ds.un("loadexception", this.onLoadError, this);
8339         ds.un("remove", this.updateInfo, this);
8340         ds.un("add", this.updateInfo, this);
8341         this.ds = undefined;
8342     },
8343
8344     /**
8345      * Binds the paging toolbar to the specified {@link Roo.data.Store}
8346      * @param {Roo.data.Store} store The data store to bind
8347      */
8348     bind : function(ds){
8349         ds.on("beforeload", this.beforeLoad, this);
8350         ds.on("load", this.onLoad, this);
8351         ds.on("loadexception", this.onLoadError, this);
8352         ds.on("remove", this.updateInfo, this);
8353         ds.on("add", this.updateInfo, this);
8354         this.ds = ds;
8355     }
8356 });/*
8357  * Based on:
8358  * Ext JS Library 1.1.1
8359  * Copyright(c) 2006-2007, Ext JS, LLC.
8360  *
8361  * Originally Released Under LGPL - original licence link has changed is not relivant.
8362  *
8363  * Fork - LGPL
8364  * <script type="text/javascript">
8365  */
8366
8367 /**
8368  * @class Roo.Resizable
8369  * @extends Roo.util.Observable
8370  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
8371  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
8372  * 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
8373  * the element will be wrapped for you automatically.</p>
8374  * <p>Here is the list of valid resize handles:</p>
8375  * <pre>
8376 Value   Description
8377 ------  -------------------
8378  'n'     north
8379  's'     south
8380  'e'     east
8381  'w'     west
8382  'nw'    northwest
8383  'sw'    southwest
8384  'se'    southeast
8385  'ne'    northeast
8386  'hd'    horizontal drag
8387  'all'   all
8388 </pre>
8389  * <p>Here's an example showing the creation of a typical Resizable:</p>
8390  * <pre><code>
8391 var resizer = new Roo.Resizable("element-id", {
8392     handles: 'all',
8393     minWidth: 200,
8394     minHeight: 100,
8395     maxWidth: 500,
8396     maxHeight: 400,
8397     pinned: true
8398 });
8399 resizer.on("resize", myHandler);
8400 </code></pre>
8401  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
8402  * resizer.east.setDisplayed(false);</p>
8403  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
8404  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
8405  * resize operation's new size (defaults to [0, 0])
8406  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
8407  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
8408  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
8409  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
8410  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
8411  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
8412  * @cfg {Number} width The width of the element in pixels (defaults to null)
8413  * @cfg {Number} height The height of the element in pixels (defaults to null)
8414  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
8415  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
8416  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
8417  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
8418  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
8419  * in favor of the handles config option (defaults to false)
8420  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
8421  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
8422  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
8423  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
8424  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
8425  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
8426  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
8427  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
8428  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
8429  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
8430  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
8431  * @constructor
8432  * Create a new resizable component
8433  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
8434  * @param {Object} config configuration options
8435   */
8436 Roo.Resizable = function(el, config)
8437 {
8438     this.el = Roo.get(el);
8439
8440     if(config && config.wrap){
8441         config.resizeChild = this.el;
8442         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
8443         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
8444         this.el.setStyle("overflow", "hidden");
8445         this.el.setPositioning(config.resizeChild.getPositioning());
8446         config.resizeChild.clearPositioning();
8447         if(!config.width || !config.height){
8448             var csize = config.resizeChild.getSize();
8449             this.el.setSize(csize.width, csize.height);
8450         }
8451         if(config.pinned && !config.adjustments){
8452             config.adjustments = "auto";
8453         }
8454     }
8455
8456     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
8457     this.proxy.unselectable();
8458     this.proxy.enableDisplayMode('block');
8459
8460     Roo.apply(this, config);
8461
8462     if(this.pinned){
8463         this.disableTrackOver = true;
8464         this.el.addClass("x-resizable-pinned");
8465     }
8466     // if the element isn't positioned, make it relative
8467     var position = this.el.getStyle("position");
8468     if(position != "absolute" && position != "fixed"){
8469         this.el.setStyle("position", "relative");
8470     }
8471     if(!this.handles){ // no handles passed, must be legacy style
8472         this.handles = 's,e,se';
8473         if(this.multiDirectional){
8474             this.handles += ',n,w';
8475         }
8476     }
8477     if(this.handles == "all"){
8478         this.handles = "n s e w ne nw se sw";
8479     }
8480     var hs = this.handles.split(/\s*?[,;]\s*?| /);
8481     var ps = Roo.Resizable.positions;
8482     for(var i = 0, len = hs.length; i < len; i++){
8483         if(hs[i] && ps[hs[i]]){
8484             var pos = ps[hs[i]];
8485             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
8486         }
8487     }
8488     // legacy
8489     this.corner = this.southeast;
8490     
8491     // updateBox = the box can move..
8492     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
8493         this.updateBox = true;
8494     }
8495
8496     this.activeHandle = null;
8497
8498     if(this.resizeChild){
8499         if(typeof this.resizeChild == "boolean"){
8500             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
8501         }else{
8502             this.resizeChild = Roo.get(this.resizeChild, true);
8503         }
8504     }
8505     
8506     if(this.adjustments == "auto"){
8507         var rc = this.resizeChild;
8508         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
8509         if(rc && (hw || hn)){
8510             rc.position("relative");
8511             rc.setLeft(hw ? hw.el.getWidth() : 0);
8512             rc.setTop(hn ? hn.el.getHeight() : 0);
8513         }
8514         this.adjustments = [
8515             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
8516             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
8517         ];
8518     }
8519
8520     if(this.draggable){
8521         this.dd = this.dynamic ?
8522             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
8523         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
8524     }
8525
8526     // public events
8527     this.addEvents({
8528         /**
8529          * @event beforeresize
8530          * Fired before resize is allowed. Set enabled to false to cancel resize.
8531          * @param {Roo.Resizable} this
8532          * @param {Roo.EventObject} e The mousedown event
8533          */
8534         "beforeresize" : true,
8535         /**
8536          * @event resizing
8537          * Fired a resizing.
8538          * @param {Roo.Resizable} this
8539          * @param {Number} x The new x position
8540          * @param {Number} y The new y position
8541          * @param {Number} w The new w width
8542          * @param {Number} h The new h hight
8543          * @param {Roo.EventObject} e The mouseup event
8544          */
8545         "resizing" : true,
8546         /**
8547          * @event resize
8548          * Fired after a resize.
8549          * @param {Roo.Resizable} this
8550          * @param {Number} width The new width
8551          * @param {Number} height The new height
8552          * @param {Roo.EventObject} e The mouseup event
8553          */
8554         "resize" : true
8555     });
8556
8557     if(this.width !== null && this.height !== null){
8558         this.resizeTo(this.width, this.height);
8559     }else{
8560         this.updateChildSize();
8561     }
8562     if(Roo.isIE){
8563         this.el.dom.style.zoom = 1;
8564     }
8565     Roo.Resizable.superclass.constructor.call(this);
8566 };
8567
8568 Roo.extend(Roo.Resizable, Roo.util.Observable, {
8569         resizeChild : false,
8570         adjustments : [0, 0],
8571         minWidth : 5,
8572         minHeight : 5,
8573         maxWidth : 10000,
8574         maxHeight : 10000,
8575         enabled : true,
8576         animate : false,
8577         duration : .35,
8578         dynamic : false,
8579         handles : false,
8580         multiDirectional : false,
8581         disableTrackOver : false,
8582         easing : 'easeOutStrong',
8583         widthIncrement : 0,
8584         heightIncrement : 0,
8585         pinned : false,
8586         width : null,
8587         height : null,
8588         preserveRatio : false,
8589         transparent: false,
8590         minX: 0,
8591         minY: 0,
8592         draggable: false,
8593
8594         /**
8595          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
8596          */
8597         constrainTo: undefined,
8598         /**
8599          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
8600          */
8601         resizeRegion: undefined,
8602
8603
8604     /**
8605      * Perform a manual resize
8606      * @param {Number} width
8607      * @param {Number} height
8608      */
8609     resizeTo : function(width, height){
8610         this.el.setSize(width, height);
8611         this.updateChildSize();
8612         this.fireEvent("resize", this, width, height, null);
8613     },
8614
8615     // private
8616     startSizing : function(e, handle){
8617         this.fireEvent("beforeresize", this, e);
8618         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
8619
8620             if(!this.overlay){
8621                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
8622                 this.overlay.unselectable();
8623                 this.overlay.enableDisplayMode("block");
8624                 this.overlay.on("mousemove", this.onMouseMove, this);
8625                 this.overlay.on("mouseup", this.onMouseUp, this);
8626             }
8627             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
8628
8629             this.resizing = true;
8630             this.startBox = this.el.getBox();
8631             this.startPoint = e.getXY();
8632             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
8633                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
8634
8635             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8636             this.overlay.show();
8637
8638             if(this.constrainTo) {
8639                 var ct = Roo.get(this.constrainTo);
8640                 this.resizeRegion = ct.getRegion().adjust(
8641                     ct.getFrameWidth('t'),
8642                     ct.getFrameWidth('l'),
8643                     -ct.getFrameWidth('b'),
8644                     -ct.getFrameWidth('r')
8645                 );
8646             }
8647
8648             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
8649             this.proxy.show();
8650             this.proxy.setBox(this.startBox);
8651             if(!this.dynamic){
8652                 this.proxy.setStyle('visibility', 'visible');
8653             }
8654         }
8655     },
8656
8657     // private
8658     onMouseDown : function(handle, e){
8659         if(this.enabled){
8660             e.stopEvent();
8661             this.activeHandle = handle;
8662             this.startSizing(e, handle);
8663         }
8664     },
8665
8666     // private
8667     onMouseUp : function(e){
8668         var size = this.resizeElement();
8669         this.resizing = false;
8670         this.handleOut();
8671         this.overlay.hide();
8672         this.proxy.hide();
8673         this.fireEvent("resize", this, size.width, size.height, e);
8674     },
8675
8676     // private
8677     updateChildSize : function(){
8678         
8679         if(this.resizeChild){
8680             var el = this.el;
8681             var child = this.resizeChild;
8682             var adj = this.adjustments;
8683             if(el.dom.offsetWidth){
8684                 var b = el.getSize(true);
8685                 child.setSize(b.width+adj[0], b.height+adj[1]);
8686             }
8687             // Second call here for IE
8688             // The first call enables instant resizing and
8689             // the second call corrects scroll bars if they
8690             // exist
8691             if(Roo.isIE){
8692                 setTimeout(function(){
8693                     if(el.dom.offsetWidth){
8694                         var b = el.getSize(true);
8695                         child.setSize(b.width+adj[0], b.height+adj[1]);
8696                     }
8697                 }, 10);
8698             }
8699         }
8700     },
8701
8702     // private
8703     snap : function(value, inc, min){
8704         if(!inc || !value) {
8705             return value;
8706         }
8707         var newValue = value;
8708         var m = value % inc;
8709         if(m > 0){
8710             if(m > (inc/2)){
8711                 newValue = value + (inc-m);
8712             }else{
8713                 newValue = value - m;
8714             }
8715         }
8716         return Math.max(min, newValue);
8717     },
8718
8719     // private
8720     resizeElement : function(){
8721         var box = this.proxy.getBox();
8722         if(this.updateBox){
8723             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
8724         }else{
8725             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
8726         }
8727         this.updateChildSize();
8728         if(!this.dynamic){
8729             this.proxy.hide();
8730         }
8731         return box;
8732     },
8733
8734     // private
8735     constrain : function(v, diff, m, mx){
8736         if(v - diff < m){
8737             diff = v - m;
8738         }else if(v - diff > mx){
8739             diff = mx - v;
8740         }
8741         return diff;
8742     },
8743
8744     // private
8745     onMouseMove : function(e){
8746         
8747         if(this.enabled){
8748             try{// try catch so if something goes wrong the user doesn't get hung
8749
8750             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
8751                 return;
8752             }
8753
8754             //var curXY = this.startPoint;
8755             var curSize = this.curSize || this.startBox;
8756             var x = this.startBox.x, y = this.startBox.y;
8757             var ox = x, oy = y;
8758             var w = curSize.width, h = curSize.height;
8759             var ow = w, oh = h;
8760             var mw = this.minWidth, mh = this.minHeight;
8761             var mxw = this.maxWidth, mxh = this.maxHeight;
8762             var wi = this.widthIncrement;
8763             var hi = this.heightIncrement;
8764
8765             var eventXY = e.getXY();
8766             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
8767             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
8768
8769             var pos = this.activeHandle.position;
8770
8771             switch(pos){
8772                 case "east":
8773                     w += diffX;
8774                     w = Math.min(Math.max(mw, w), mxw);
8775                     break;
8776              
8777                 case "south":
8778                     h += diffY;
8779                     h = Math.min(Math.max(mh, h), mxh);
8780                     break;
8781                 case "southeast":
8782                     w += diffX;
8783                     h += diffY;
8784                     w = Math.min(Math.max(mw, w), mxw);
8785                     h = Math.min(Math.max(mh, h), mxh);
8786                     break;
8787                 case "north":
8788                     diffY = this.constrain(h, diffY, mh, mxh);
8789                     y += diffY;
8790                     h -= diffY;
8791                     break;
8792                 case "hdrag":
8793                     
8794                     if (wi) {
8795                         var adiffX = Math.abs(diffX);
8796                         var sub = (adiffX % wi); // how much 
8797                         if (sub > (wi/2)) { // far enough to snap
8798                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
8799                         } else {
8800                             // remove difference.. 
8801                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
8802                         }
8803                     }
8804                     x += diffX;
8805                     x = Math.max(this.minX, x);
8806                     break;
8807                 case "west":
8808                     diffX = this.constrain(w, diffX, mw, mxw);
8809                     x += diffX;
8810                     w -= diffX;
8811                     break;
8812                 case "northeast":
8813                     w += diffX;
8814                     w = Math.min(Math.max(mw, w), mxw);
8815                     diffY = this.constrain(h, diffY, mh, mxh);
8816                     y += diffY;
8817                     h -= diffY;
8818                     break;
8819                 case "northwest":
8820                     diffX = this.constrain(w, diffX, mw, mxw);
8821                     diffY = this.constrain(h, diffY, mh, mxh);
8822                     y += diffY;
8823                     h -= diffY;
8824                     x += diffX;
8825                     w -= diffX;
8826                     break;
8827                case "southwest":
8828                     diffX = this.constrain(w, diffX, mw, mxw);
8829                     h += diffY;
8830                     h = Math.min(Math.max(mh, h), mxh);
8831                     x += diffX;
8832                     w -= diffX;
8833                     break;
8834             }
8835
8836             var sw = this.snap(w, wi, mw);
8837             var sh = this.snap(h, hi, mh);
8838             if(sw != w || sh != h){
8839                 switch(pos){
8840                     case "northeast":
8841                         y -= sh - h;
8842                     break;
8843                     case "north":
8844                         y -= sh - h;
8845                         break;
8846                     case "southwest":
8847                         x -= sw - w;
8848                     break;
8849                     case "west":
8850                         x -= sw - w;
8851                         break;
8852                     case "northwest":
8853                         x -= sw - w;
8854                         y -= sh - h;
8855                     break;
8856                 }
8857                 w = sw;
8858                 h = sh;
8859             }
8860
8861             if(this.preserveRatio){
8862                 switch(pos){
8863                     case "southeast":
8864                     case "east":
8865                         h = oh * (w/ow);
8866                         h = Math.min(Math.max(mh, h), mxh);
8867                         w = ow * (h/oh);
8868                        break;
8869                     case "south":
8870                         w = ow * (h/oh);
8871                         w = Math.min(Math.max(mw, w), mxw);
8872                         h = oh * (w/ow);
8873                         break;
8874                     case "northeast":
8875                         w = ow * (h/oh);
8876                         w = Math.min(Math.max(mw, w), mxw);
8877                         h = oh * (w/ow);
8878                     break;
8879                     case "north":
8880                         var tw = w;
8881                         w = ow * (h/oh);
8882                         w = Math.min(Math.max(mw, w), mxw);
8883                         h = oh * (w/ow);
8884                         x += (tw - w) / 2;
8885                         break;
8886                     case "southwest":
8887                         h = oh * (w/ow);
8888                         h = Math.min(Math.max(mh, h), mxh);
8889                         var tw = w;
8890                         w = ow * (h/oh);
8891                         x += tw - w;
8892                         break;
8893                     case "west":
8894                         var th = h;
8895                         h = oh * (w/ow);
8896                         h = Math.min(Math.max(mh, h), mxh);
8897                         y += (th - h) / 2;
8898                         var tw = w;
8899                         w = ow * (h/oh);
8900                         x += tw - w;
8901                        break;
8902                     case "northwest":
8903                         var tw = w;
8904                         var th = h;
8905                         h = oh * (w/ow);
8906                         h = Math.min(Math.max(mh, h), mxh);
8907                         w = ow * (h/oh);
8908                         y += th - h;
8909                         x += tw - w;
8910                        break;
8911
8912                 }
8913             }
8914             if (pos == 'hdrag') {
8915                 w = ow;
8916             }
8917             this.proxy.setBounds(x, y, w, h);
8918             if(this.dynamic){
8919                 this.resizeElement();
8920             }
8921             }catch(e){}
8922         }
8923         this.fireEvent("resizing", this, x, y, w, h, e);
8924     },
8925
8926     // private
8927     handleOver : function(){
8928         if(this.enabled){
8929             this.el.addClass("x-resizable-over");
8930         }
8931     },
8932
8933     // private
8934     handleOut : function(){
8935         if(!this.resizing){
8936             this.el.removeClass("x-resizable-over");
8937         }
8938     },
8939
8940     /**
8941      * Returns the element this component is bound to.
8942      * @return {Roo.Element}
8943      */
8944     getEl : function(){
8945         return this.el;
8946     },
8947
8948     /**
8949      * Returns the resizeChild element (or null).
8950      * @return {Roo.Element}
8951      */
8952     getResizeChild : function(){
8953         return this.resizeChild;
8954     },
8955     groupHandler : function()
8956     {
8957         
8958     },
8959     /**
8960      * Destroys this resizable. If the element was wrapped and
8961      * removeEl is not true then the element remains.
8962      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
8963      */
8964     destroy : function(removeEl){
8965         this.proxy.remove();
8966         if(this.overlay){
8967             this.overlay.removeAllListeners();
8968             this.overlay.remove();
8969         }
8970         var ps = Roo.Resizable.positions;
8971         for(var k in ps){
8972             if(typeof ps[k] != "function" && this[ps[k]]){
8973                 var h = this[ps[k]];
8974                 h.el.removeAllListeners();
8975                 h.el.remove();
8976             }
8977         }
8978         if(removeEl){
8979             this.el.update("");
8980             this.el.remove();
8981         }
8982     }
8983 });
8984
8985 // private
8986 // hash to map config positions to true positions
8987 Roo.Resizable.positions = {
8988     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
8989     hd: "hdrag"
8990 };
8991
8992 // private
8993 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
8994     if(!this.tpl){
8995         // only initialize the template if resizable is used
8996         var tpl = Roo.DomHelper.createTemplate(
8997             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
8998         );
8999         tpl.compile();
9000         Roo.Resizable.Handle.prototype.tpl = tpl;
9001     }
9002     this.position = pos;
9003     this.rz = rz;
9004     // show north drag fro topdra
9005     var handlepos = pos == 'hdrag' ? 'north' : pos;
9006     
9007     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
9008     if (pos == 'hdrag') {
9009         this.el.setStyle('cursor', 'pointer');
9010     }
9011     this.el.unselectable();
9012     if(transparent){
9013         this.el.setOpacity(0);
9014     }
9015     this.el.on("mousedown", this.onMouseDown, this);
9016     if(!disableTrackOver){
9017         this.el.on("mouseover", this.onMouseOver, this);
9018         this.el.on("mouseout", this.onMouseOut, this);
9019     }
9020 };
9021
9022 // private
9023 Roo.Resizable.Handle.prototype = {
9024     afterResize : function(rz){
9025         Roo.log('after?');
9026         // do nothing
9027     },
9028     // private
9029     onMouseDown : function(e){
9030         this.rz.onMouseDown(this, e);
9031     },
9032     // private
9033     onMouseOver : function(e){
9034         this.rz.handleOver(this, e);
9035     },
9036     // private
9037     onMouseOut : function(e){
9038         this.rz.handleOut(this, e);
9039     }
9040 };/*
9041  * Based on:
9042  * Ext JS Library 1.1.1
9043  * Copyright(c) 2006-2007, Ext JS, LLC.
9044  *
9045  * Originally Released Under LGPL - original licence link has changed is not relivant.
9046  *
9047  * Fork - LGPL
9048  * <script type="text/javascript">
9049  */
9050
9051 /**
9052  * @class Roo.Editor
9053  * @extends Roo.Component
9054  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
9055  * @constructor
9056  * Create a new Editor
9057  * @param {Roo.form.Field} field The Field object (or descendant)
9058  * @param {Object} config The config object
9059  */
9060 Roo.Editor = function(field, config){
9061     Roo.Editor.superclass.constructor.call(this, config);
9062     this.field = field;
9063     this.addEvents({
9064         /**
9065              * @event beforestartedit
9066              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
9067              * false from the handler of this event.
9068              * @param {Editor} this
9069              * @param {Roo.Element} boundEl The underlying element bound to this editor
9070              * @param {Mixed} value The field value being set
9071              */
9072         "beforestartedit" : true,
9073         /**
9074              * @event startedit
9075              * Fires when this editor is displayed
9076              * @param {Roo.Element} boundEl The underlying element bound to this editor
9077              * @param {Mixed} value The starting field value
9078              */
9079         "startedit" : true,
9080         /**
9081              * @event beforecomplete
9082              * Fires after a change has been made to the field, but before the change is reflected in the underlying
9083              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
9084              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
9085              * event will not fire since no edit actually occurred.
9086              * @param {Editor} this
9087              * @param {Mixed} value The current field value
9088              * @param {Mixed} startValue The original field value
9089              */
9090         "beforecomplete" : true,
9091         /**
9092              * @event complete
9093              * Fires after editing is complete and any changed value has been written to the underlying field.
9094              * @param {Editor} this
9095              * @param {Mixed} value The current field value
9096              * @param {Mixed} startValue The original field value
9097              */
9098         "complete" : true,
9099         /**
9100          * @event specialkey
9101          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
9102          * {@link Roo.EventObject#getKey} to determine which key was pressed.
9103          * @param {Roo.form.Field} this
9104          * @param {Roo.EventObject} e The event object
9105          */
9106         "specialkey" : true
9107     });
9108 };
9109
9110 Roo.extend(Roo.Editor, Roo.Component, {
9111     /**
9112      * @cfg {Boolean/String} autosize
9113      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
9114      * or "height" to adopt the height only (defaults to false)
9115      */
9116     /**
9117      * @cfg {Boolean} revertInvalid
9118      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
9119      * validation fails (defaults to true)
9120      */
9121     /**
9122      * @cfg {Boolean} ignoreNoChange
9123      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
9124      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
9125      * will never be ignored.
9126      */
9127     /**
9128      * @cfg {Boolean} hideEl
9129      * False to keep the bound element visible while the editor is displayed (defaults to true)
9130      */
9131     /**
9132      * @cfg {Mixed} value
9133      * The data value of the underlying field (defaults to "")
9134      */
9135     value : "",
9136     /**
9137      * @cfg {String} alignment
9138      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
9139      */
9140     alignment: "c-c?",
9141     /**
9142      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
9143      * for bottom-right shadow (defaults to "frame")
9144      */
9145     shadow : "frame",
9146     /**
9147      * @cfg {Boolean} constrain True to constrain the editor to the viewport
9148      */
9149     constrain : false,
9150     /**
9151      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
9152      */
9153     completeOnEnter : false,
9154     /**
9155      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
9156      */
9157     cancelOnEsc : false,
9158     /**
9159      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
9160      */
9161     updateEl : false,
9162
9163     // private
9164     onRender : function(ct, position){
9165         this.el = new Roo.Layer({
9166             shadow: this.shadow,
9167             cls: "x-editor",
9168             parentEl : ct,
9169             shim : this.shim,
9170             shadowOffset:4,
9171             id: this.id,
9172             constrain: this.constrain
9173         });
9174         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
9175         if(this.field.msgTarget != 'title'){
9176             this.field.msgTarget = 'qtip';
9177         }
9178         this.field.render(this.el);
9179         if(Roo.isGecko){
9180             this.field.el.dom.setAttribute('autocomplete', 'off');
9181         }
9182         this.field.on("specialkey", this.onSpecialKey, this);
9183         if(this.swallowKeys){
9184             this.field.el.swallowEvent(['keydown','keypress']);
9185         }
9186         this.field.show();
9187         this.field.on("blur", this.onBlur, this);
9188         if(this.field.grow){
9189             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
9190         }
9191     },
9192
9193     onSpecialKey : function(field, e)
9194     {
9195         //Roo.log('editor onSpecialKey');
9196         if(this.completeOnEnter && e.getKey() == e.ENTER){
9197             e.stopEvent();
9198             this.completeEdit();
9199             return;
9200         }
9201         // do not fire special key otherwise it might hide close the editor...
9202         if(e.getKey() == e.ENTER){    
9203             return;
9204         }
9205         if(this.cancelOnEsc && e.getKey() == e.ESC){
9206             this.cancelEdit();
9207             return;
9208         } 
9209         this.fireEvent('specialkey', field, e);
9210     
9211     },
9212
9213     /**
9214      * Starts the editing process and shows the editor.
9215      * @param {String/HTMLElement/Element} el The element to edit
9216      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
9217       * to the innerHTML of el.
9218      */
9219     startEdit : function(el, value){
9220         if(this.editing){
9221             this.completeEdit();
9222         }
9223         this.boundEl = Roo.get(el);
9224         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
9225         if(!this.rendered){
9226             this.render(this.parentEl || document.body);
9227         }
9228         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
9229             return;
9230         }
9231         this.startValue = v;
9232         this.field.setValue(v);
9233         if(this.autoSize){
9234             var sz = this.boundEl.getSize();
9235             switch(this.autoSize){
9236                 case "width":
9237                 this.setSize(sz.width,  "");
9238                 break;
9239                 case "height":
9240                 this.setSize("",  sz.height);
9241                 break;
9242                 default:
9243                 this.setSize(sz.width,  sz.height);
9244             }
9245         }
9246         this.el.alignTo(this.boundEl, this.alignment);
9247         this.editing = true;
9248         if(Roo.QuickTips){
9249             Roo.QuickTips.disable();
9250         }
9251         this.show();
9252     },
9253
9254     /**
9255      * Sets the height and width of this editor.
9256      * @param {Number} width The new width
9257      * @param {Number} height The new height
9258      */
9259     setSize : function(w, h){
9260         this.field.setSize(w, h);
9261         if(this.el){
9262             this.el.sync();
9263         }
9264     },
9265
9266     /**
9267      * Realigns the editor to the bound field based on the current alignment config value.
9268      */
9269     realign : function(){
9270         this.el.alignTo(this.boundEl, this.alignment);
9271     },
9272
9273     /**
9274      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
9275      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
9276      */
9277     completeEdit : function(remainVisible){
9278         if(!this.editing){
9279             return;
9280         }
9281         var v = this.getValue();
9282         if(this.revertInvalid !== false && !this.field.isValid()){
9283             v = this.startValue;
9284             this.cancelEdit(true);
9285         }
9286         if(String(v) === String(this.startValue) && this.ignoreNoChange){
9287             this.editing = false;
9288             this.hide();
9289             return;
9290         }
9291         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
9292             this.editing = false;
9293             if(this.updateEl && this.boundEl){
9294                 this.boundEl.update(v);
9295             }
9296             if(remainVisible !== true){
9297                 this.hide();
9298             }
9299             this.fireEvent("complete", this, v, this.startValue);
9300         }
9301     },
9302
9303     // private
9304     onShow : function(){
9305         this.el.show();
9306         if(this.hideEl !== false){
9307             this.boundEl.hide();
9308         }
9309         this.field.show();
9310         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
9311             this.fixIEFocus = true;
9312             this.deferredFocus.defer(50, this);
9313         }else{
9314             this.field.focus();
9315         }
9316         this.fireEvent("startedit", this.boundEl, this.startValue);
9317     },
9318
9319     deferredFocus : function(){
9320         if(this.editing){
9321             this.field.focus();
9322         }
9323     },
9324
9325     /**
9326      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
9327      * reverted to the original starting value.
9328      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
9329      * cancel (defaults to false)
9330      */
9331     cancelEdit : function(remainVisible){
9332         if(this.editing){
9333             this.setValue(this.startValue);
9334             if(remainVisible !== true){
9335                 this.hide();
9336             }
9337         }
9338     },
9339
9340     // private
9341     onBlur : function(){
9342         if(this.allowBlur !== true && this.editing){
9343             this.completeEdit();
9344         }
9345     },
9346
9347     // private
9348     onHide : function(){
9349         if(this.editing){
9350             this.completeEdit();
9351             return;
9352         }
9353         this.field.blur();
9354         if(this.field.collapse){
9355             this.field.collapse();
9356         }
9357         this.el.hide();
9358         if(this.hideEl !== false){
9359             this.boundEl.show();
9360         }
9361         if(Roo.QuickTips){
9362             Roo.QuickTips.enable();
9363         }
9364     },
9365
9366     /**
9367      * Sets the data value of the editor
9368      * @param {Mixed} value Any valid value supported by the underlying field
9369      */
9370     setValue : function(v){
9371         this.field.setValue(v);
9372     },
9373
9374     /**
9375      * Gets the data value of the editor
9376      * @return {Mixed} The data value
9377      */
9378     getValue : function(){
9379         return this.field.getValue();
9380     }
9381 });/*
9382  * Based on:
9383  * Ext JS Library 1.1.1
9384  * Copyright(c) 2006-2007, Ext JS, LLC.
9385  *
9386  * Originally Released Under LGPL - original licence link has changed is not relivant.
9387  *
9388  * Fork - LGPL
9389  * <script type="text/javascript">
9390  */
9391  
9392 /**
9393  * @class Roo.BasicDialog
9394  * @extends Roo.util.Observable
9395  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
9396  * <pre><code>
9397 var dlg = new Roo.BasicDialog("my-dlg", {
9398     height: 200,
9399     width: 300,
9400     minHeight: 100,
9401     minWidth: 150,
9402     modal: true,
9403     proxyDrag: true,
9404     shadow: true
9405 });
9406 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
9407 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
9408 dlg.addButton('Cancel', dlg.hide, dlg);
9409 dlg.show();
9410 </code></pre>
9411   <b>A Dialog should always be a direct child of the body element.</b>
9412  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
9413  * @cfg {String} title Default text to display in the title bar (defaults to null)
9414  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9415  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9416  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
9417  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
9418  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
9419  * (defaults to null with no animation)
9420  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
9421  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
9422  * property for valid values (defaults to 'all')
9423  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
9424  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
9425  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
9426  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
9427  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
9428  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
9429  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
9430  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
9431  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
9432  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
9433  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
9434  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
9435  * draggable = true (defaults to false)
9436  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
9437  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
9438  * shadow (defaults to false)
9439  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
9440  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
9441  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
9442  * @cfg {Array} buttons Array of buttons
9443  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
9444  * @constructor
9445  * Create a new BasicDialog.
9446  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
9447  * @param {Object} config Configuration options
9448  */
9449 Roo.BasicDialog = function(el, config){
9450     this.el = Roo.get(el);
9451     var dh = Roo.DomHelper;
9452     if(!this.el && config && config.autoCreate){
9453         if(typeof config.autoCreate == "object"){
9454             if(!config.autoCreate.id){
9455                 config.autoCreate.id = el;
9456             }
9457             this.el = dh.append(document.body,
9458                         config.autoCreate, true);
9459         }else{
9460             this.el = dh.append(document.body,
9461                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
9462         }
9463     }
9464     el = this.el;
9465     el.setDisplayed(true);
9466     el.hide = this.hideAction;
9467     this.id = el.id;
9468     el.addClass("x-dlg");
9469
9470     Roo.apply(this, config);
9471
9472     this.proxy = el.createProxy("x-dlg-proxy");
9473     this.proxy.hide = this.hideAction;
9474     this.proxy.setOpacity(.5);
9475     this.proxy.hide();
9476
9477     if(config.width){
9478         el.setWidth(config.width);
9479     }
9480     if(config.height){
9481         el.setHeight(config.height);
9482     }
9483     this.size = el.getSize();
9484     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
9485         this.xy = [config.x,config.y];
9486     }else{
9487         this.xy = el.getCenterXY(true);
9488     }
9489     /** The header element @type Roo.Element */
9490     this.header = el.child("> .x-dlg-hd");
9491     /** The body element @type Roo.Element */
9492     this.body = el.child("> .x-dlg-bd");
9493     /** The footer element @type Roo.Element */
9494     this.footer = el.child("> .x-dlg-ft");
9495
9496     if(!this.header){
9497         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
9498     }
9499     if(!this.body){
9500         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
9501     }
9502
9503     this.header.unselectable();
9504     if(this.title){
9505         this.header.update(this.title);
9506     }
9507     // this element allows the dialog to be focused for keyboard event
9508     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
9509     this.focusEl.swallowEvent("click", true);
9510
9511     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
9512
9513     // wrap the body and footer for special rendering
9514     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
9515     if(this.footer){
9516         this.bwrap.dom.appendChild(this.footer.dom);
9517     }
9518
9519     this.bg = this.el.createChild({
9520         tag: "div", cls:"x-dlg-bg",
9521         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
9522     });
9523     this.centerBg = this.bg.child("div.x-dlg-bg-center");
9524
9525
9526     if(this.autoScroll !== false && !this.autoTabs){
9527         this.body.setStyle("overflow", "auto");
9528     }
9529
9530     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
9531
9532     if(this.closable !== false){
9533         this.el.addClass("x-dlg-closable");
9534         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
9535         this.close.on("click", this.closeClick, this);
9536         this.close.addClassOnOver("x-dlg-close-over");
9537     }
9538     if(this.collapsible !== false){
9539         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
9540         this.collapseBtn.on("click", this.collapseClick, this);
9541         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
9542         this.header.on("dblclick", this.collapseClick, this);
9543     }
9544     if(this.resizable !== false){
9545         this.el.addClass("x-dlg-resizable");
9546         this.resizer = new Roo.Resizable(el, {
9547             minWidth: this.minWidth || 80,
9548             minHeight:this.minHeight || 80,
9549             handles: this.resizeHandles || "all",
9550             pinned: true
9551         });
9552         this.resizer.on("beforeresize", this.beforeResize, this);
9553         this.resizer.on("resize", this.onResize, this);
9554     }
9555     if(this.draggable !== false){
9556         el.addClass("x-dlg-draggable");
9557         if (!this.proxyDrag) {
9558             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
9559         }
9560         else {
9561             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
9562         }
9563         dd.setHandleElId(this.header.id);
9564         dd.endDrag = this.endMove.createDelegate(this);
9565         dd.startDrag = this.startMove.createDelegate(this);
9566         dd.onDrag = this.onDrag.createDelegate(this);
9567         dd.scroll = false;
9568         this.dd = dd;
9569     }
9570     if(this.modal){
9571         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
9572         this.mask.enableDisplayMode("block");
9573         this.mask.hide();
9574         this.el.addClass("x-dlg-modal");
9575     }
9576     if(this.shadow){
9577         this.shadow = new Roo.Shadow({
9578             mode : typeof this.shadow == "string" ? this.shadow : "sides",
9579             offset : this.shadowOffset
9580         });
9581     }else{
9582         this.shadowOffset = 0;
9583     }
9584     if(Roo.useShims && this.shim !== false){
9585         this.shim = this.el.createShim();
9586         this.shim.hide = this.hideAction;
9587         this.shim.hide();
9588     }else{
9589         this.shim = false;
9590     }
9591     if(this.autoTabs){
9592         this.initTabs();
9593     }
9594     if (this.buttons) { 
9595         var bts= this.buttons;
9596         this.buttons = [];
9597         Roo.each(bts, function(b) {
9598             this.addButton(b);
9599         }, this);
9600     }
9601     
9602     
9603     this.addEvents({
9604         /**
9605          * @event keydown
9606          * Fires when a key is pressed
9607          * @param {Roo.BasicDialog} this
9608          * @param {Roo.EventObject} e
9609          */
9610         "keydown" : true,
9611         /**
9612          * @event move
9613          * Fires when this dialog is moved by the user.
9614          * @param {Roo.BasicDialog} this
9615          * @param {Number} x The new page X
9616          * @param {Number} y The new page Y
9617          */
9618         "move" : true,
9619         /**
9620          * @event resize
9621          * Fires when this dialog is resized by the user.
9622          * @param {Roo.BasicDialog} this
9623          * @param {Number} width The new width
9624          * @param {Number} height The new height
9625          */
9626         "resize" : true,
9627         /**
9628          * @event beforehide
9629          * Fires before this dialog is hidden.
9630          * @param {Roo.BasicDialog} this
9631          */
9632         "beforehide" : true,
9633         /**
9634          * @event hide
9635          * Fires when this dialog is hidden.
9636          * @param {Roo.BasicDialog} this
9637          */
9638         "hide" : true,
9639         /**
9640          * @event beforeshow
9641          * Fires before this dialog is shown.
9642          * @param {Roo.BasicDialog} this
9643          */
9644         "beforeshow" : true,
9645         /**
9646          * @event show
9647          * Fires when this dialog is shown.
9648          * @param {Roo.BasicDialog} this
9649          */
9650         "show" : true
9651     });
9652     el.on("keydown", this.onKeyDown, this);
9653     el.on("mousedown", this.toFront, this);
9654     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
9655     this.el.hide();
9656     Roo.DialogManager.register(this);
9657     Roo.BasicDialog.superclass.constructor.call(this);
9658 };
9659
9660 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
9661     shadowOffset: Roo.isIE ? 6 : 5,
9662     minHeight: 80,
9663     minWidth: 200,
9664     minButtonWidth: 75,
9665     defaultButton: null,
9666     buttonAlign: "right",
9667     tabTag: 'div',
9668     firstShow: true,
9669
9670     /**
9671      * Sets the dialog title text
9672      * @param {String} text The title text to display
9673      * @return {Roo.BasicDialog} this
9674      */
9675     setTitle : function(text){
9676         this.header.update(text);
9677         return this;
9678     },
9679
9680     // private
9681     closeClick : function(){
9682         this.hide();
9683     },
9684
9685     // private
9686     collapseClick : function(){
9687         this[this.collapsed ? "expand" : "collapse"]();
9688     },
9689
9690     /**
9691      * Collapses the dialog to its minimized state (only the title bar is visible).
9692      * Equivalent to the user clicking the collapse dialog button.
9693      */
9694     collapse : function(){
9695         if(!this.collapsed){
9696             this.collapsed = true;
9697             this.el.addClass("x-dlg-collapsed");
9698             this.restoreHeight = this.el.getHeight();
9699             this.resizeTo(this.el.getWidth(), this.header.getHeight());
9700         }
9701     },
9702
9703     /**
9704      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
9705      * clicking the expand dialog button.
9706      */
9707     expand : function(){
9708         if(this.collapsed){
9709             this.collapsed = false;
9710             this.el.removeClass("x-dlg-collapsed");
9711             this.resizeTo(this.el.getWidth(), this.restoreHeight);
9712         }
9713     },
9714
9715     /**
9716      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
9717      * @return {Roo.TabPanel} The tabs component
9718      */
9719     initTabs : function(){
9720         var tabs = this.getTabs();
9721         while(tabs.getTab(0)){
9722             tabs.removeTab(0);
9723         }
9724         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
9725             var dom = el.dom;
9726             tabs.addTab(Roo.id(dom), dom.title);
9727             dom.title = "";
9728         });
9729         tabs.activate(0);
9730         return tabs;
9731     },
9732
9733     // private
9734     beforeResize : function(){
9735         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
9736     },
9737
9738     // private
9739     onResize : function(){
9740         this.refreshSize();
9741         this.syncBodyHeight();
9742         this.adjustAssets();
9743         this.focus();
9744         this.fireEvent("resize", this, this.size.width, this.size.height);
9745     },
9746
9747     // private
9748     onKeyDown : function(e){
9749         if(this.isVisible()){
9750             this.fireEvent("keydown", this, e);
9751         }
9752     },
9753
9754     /**
9755      * Resizes the dialog.
9756      * @param {Number} width
9757      * @param {Number} height
9758      * @return {Roo.BasicDialog} this
9759      */
9760     resizeTo : function(width, height){
9761         this.el.setSize(width, height);
9762         this.size = {width: width, height: height};
9763         this.syncBodyHeight();
9764         if(this.fixedcenter){
9765             this.center();
9766         }
9767         if(this.isVisible()){
9768             this.constrainXY();
9769             this.adjustAssets();
9770         }
9771         this.fireEvent("resize", this, width, height);
9772         return this;
9773     },
9774
9775
9776     /**
9777      * Resizes the dialog to fit the specified content size.
9778      * @param {Number} width
9779      * @param {Number} height
9780      * @return {Roo.BasicDialog} this
9781      */
9782     setContentSize : function(w, h){
9783         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
9784         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
9785         //if(!this.el.isBorderBox()){
9786             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
9787             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
9788         //}
9789         if(this.tabs){
9790             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
9791             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
9792         }
9793         this.resizeTo(w, h);
9794         return this;
9795     },
9796
9797     /**
9798      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
9799      * executed in response to a particular key being pressed while the dialog is active.
9800      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
9801      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9802      * @param {Function} fn The function to call
9803      * @param {Object} scope (optional) The scope of the function
9804      * @return {Roo.BasicDialog} this
9805      */
9806     addKeyListener : function(key, fn, scope){
9807         var keyCode, shift, ctrl, alt;
9808         if(typeof key == "object" && !(key instanceof Array)){
9809             keyCode = key["key"];
9810             shift = key["shift"];
9811             ctrl = key["ctrl"];
9812             alt = key["alt"];
9813         }else{
9814             keyCode = key;
9815         }
9816         var handler = function(dlg, e){
9817             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
9818                 var k = e.getKey();
9819                 if(keyCode instanceof Array){
9820                     for(var i = 0, len = keyCode.length; i < len; i++){
9821                         if(keyCode[i] == k){
9822                           fn.call(scope || window, dlg, k, e);
9823                           return;
9824                         }
9825                     }
9826                 }else{
9827                     if(k == keyCode){
9828                         fn.call(scope || window, dlg, k, e);
9829                     }
9830                 }
9831             }
9832         };
9833         this.on("keydown", handler);
9834         return this;
9835     },
9836
9837     /**
9838      * Returns the TabPanel component (creates it if it doesn't exist).
9839      * Note: If you wish to simply check for the existence of tabs without creating them,
9840      * check for a null 'tabs' property.
9841      * @return {Roo.TabPanel} The tabs component
9842      */
9843     getTabs : function(){
9844         if(!this.tabs){
9845             this.el.addClass("x-dlg-auto-tabs");
9846             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
9847             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
9848         }
9849         return this.tabs;
9850     },
9851
9852     /**
9853      * Adds a button to the footer section of the dialog.
9854      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
9855      * object or a valid Roo.DomHelper element config
9856      * @param {Function} handler The function called when the button is clicked
9857      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
9858      * @return {Roo.Button} The new button
9859      */
9860     addButton : function(config, handler, scope){
9861         var dh = Roo.DomHelper;
9862         if(!this.footer){
9863             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
9864         }
9865         if(!this.btnContainer){
9866             var tb = this.footer.createChild({
9867
9868                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
9869                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
9870             }, null, true);
9871             this.btnContainer = tb.firstChild.firstChild.firstChild;
9872         }
9873         var bconfig = {
9874             handler: handler,
9875             scope: scope,
9876             minWidth: this.minButtonWidth,
9877             hideParent:true
9878         };
9879         if(typeof config == "string"){
9880             bconfig.text = config;
9881         }else{
9882             if(config.tag){
9883                 bconfig.dhconfig = config;
9884             }else{
9885                 Roo.apply(bconfig, config);
9886             }
9887         }
9888         var fc = false;
9889         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
9890             bconfig.position = Math.max(0, bconfig.position);
9891             fc = this.btnContainer.childNodes[bconfig.position];
9892         }
9893          
9894         var btn = new Roo.Button(
9895             fc ? 
9896                 this.btnContainer.insertBefore(document.createElement("td"),fc)
9897                 : this.btnContainer.appendChild(document.createElement("td")),
9898             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
9899             bconfig
9900         );
9901         this.syncBodyHeight();
9902         if(!this.buttons){
9903             /**
9904              * Array of all the buttons that have been added to this dialog via addButton
9905              * @type Array
9906              */
9907             this.buttons = [];
9908         }
9909         this.buttons.push(btn);
9910         return btn;
9911     },
9912
9913     /**
9914      * Sets the default button to be focused when the dialog is displayed.
9915      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
9916      * @return {Roo.BasicDialog} this
9917      */
9918     setDefaultButton : function(btn){
9919         this.defaultButton = btn;
9920         return this;
9921     },
9922
9923     // private
9924     getHeaderFooterHeight : function(safe){
9925         var height = 0;
9926         if(this.header){
9927            height += this.header.getHeight();
9928         }
9929         if(this.footer){
9930            var fm = this.footer.getMargins();
9931             height += (this.footer.getHeight()+fm.top+fm.bottom);
9932         }
9933         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
9934         height += this.centerBg.getPadding("tb");
9935         return height;
9936     },
9937
9938     // private
9939     syncBodyHeight : function()
9940     {
9941         var bd = this.body, // the text
9942             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
9943             bw = this.bwrap;
9944         var height = this.size.height - this.getHeaderFooterHeight(false);
9945         bd.setHeight(height-bd.getMargins("tb"));
9946         var hh = this.header.getHeight();
9947         var h = this.size.height-hh;
9948         cb.setHeight(h);
9949         
9950         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
9951         bw.setHeight(h-cb.getPadding("tb"));
9952         
9953         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
9954         bd.setWidth(bw.getWidth(true));
9955         if(this.tabs){
9956             this.tabs.syncHeight();
9957             if(Roo.isIE){
9958                 this.tabs.el.repaint();
9959             }
9960         }
9961     },
9962
9963     /**
9964      * Restores the previous state of the dialog if Roo.state is configured.
9965      * @return {Roo.BasicDialog} this
9966      */
9967     restoreState : function(){
9968         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
9969         if(box && box.width){
9970             this.xy = [box.x, box.y];
9971             this.resizeTo(box.width, box.height);
9972         }
9973         return this;
9974     },
9975
9976     // private
9977     beforeShow : function(){
9978         this.expand();
9979         if(this.fixedcenter){
9980             this.xy = this.el.getCenterXY(true);
9981         }
9982         if(this.modal){
9983             Roo.get(document.body).addClass("x-body-masked");
9984             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
9985             this.mask.show();
9986         }
9987         this.constrainXY();
9988     },
9989
9990     // private
9991     animShow : function(){
9992         var b = Roo.get(this.animateTarget).getBox();
9993         this.proxy.setSize(b.width, b.height);
9994         this.proxy.setLocation(b.x, b.y);
9995         this.proxy.show();
9996         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
9997                     true, .35, this.showEl.createDelegate(this));
9998     },
9999
10000     /**
10001      * Shows the dialog.
10002      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
10003      * @return {Roo.BasicDialog} this
10004      */
10005     show : function(animateTarget){
10006         if (this.fireEvent("beforeshow", this) === false){
10007             return;
10008         }
10009         if(this.syncHeightBeforeShow){
10010             this.syncBodyHeight();
10011         }else if(this.firstShow){
10012             this.firstShow = false;
10013             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
10014         }
10015         this.animateTarget = animateTarget || this.animateTarget;
10016         if(!this.el.isVisible()){
10017             this.beforeShow();
10018             if(this.animateTarget && Roo.get(this.animateTarget)){
10019                 this.animShow();
10020             }else{
10021                 this.showEl();
10022             }
10023         }
10024         return this;
10025     },
10026
10027     // private
10028     showEl : function(){
10029         this.proxy.hide();
10030         this.el.setXY(this.xy);
10031         this.el.show();
10032         this.adjustAssets(true);
10033         this.toFront();
10034         this.focus();
10035         // IE peekaboo bug - fix found by Dave Fenwick
10036         if(Roo.isIE){
10037             this.el.repaint();
10038         }
10039         this.fireEvent("show", this);
10040     },
10041
10042     /**
10043      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
10044      * dialog itself will receive focus.
10045      */
10046     focus : function(){
10047         if(this.defaultButton){
10048             this.defaultButton.focus();
10049         }else{
10050             this.focusEl.focus();
10051         }
10052     },
10053
10054     // private
10055     constrainXY : function(){
10056         if(this.constraintoviewport !== false){
10057             if(!this.viewSize){
10058                 if(this.container){
10059                     var s = this.container.getSize();
10060                     this.viewSize = [s.width, s.height];
10061                 }else{
10062                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
10063                 }
10064             }
10065             var s = Roo.get(this.container||document).getScroll();
10066
10067             var x = this.xy[0], y = this.xy[1];
10068             var w = this.size.width, h = this.size.height;
10069             var vw = this.viewSize[0], vh = this.viewSize[1];
10070             // only move it if it needs it
10071             var moved = false;
10072             // first validate right/bottom
10073             if(x + w > vw+s.left){
10074                 x = vw - w;
10075                 moved = true;
10076             }
10077             if(y + h > vh+s.top){
10078                 y = vh - h;
10079                 moved = true;
10080             }
10081             // then make sure top/left isn't negative
10082             if(x < s.left){
10083                 x = s.left;
10084                 moved = true;
10085             }
10086             if(y < s.top){
10087                 y = s.top;
10088                 moved = true;
10089             }
10090             if(moved){
10091                 // cache xy
10092                 this.xy = [x, y];
10093                 if(this.isVisible()){
10094                     this.el.setLocation(x, y);
10095                     this.adjustAssets();
10096                 }
10097             }
10098         }
10099     },
10100
10101     // private
10102     onDrag : function(){
10103         if(!this.proxyDrag){
10104             this.xy = this.el.getXY();
10105             this.adjustAssets();
10106         }
10107     },
10108
10109     // private
10110     adjustAssets : function(doShow){
10111         var x = this.xy[0], y = this.xy[1];
10112         var w = this.size.width, h = this.size.height;
10113         if(doShow === true){
10114             if(this.shadow){
10115                 this.shadow.show(this.el);
10116             }
10117             if(this.shim){
10118                 this.shim.show();
10119             }
10120         }
10121         if(this.shadow && this.shadow.isVisible()){
10122             this.shadow.show(this.el);
10123         }
10124         if(this.shim && this.shim.isVisible()){
10125             this.shim.setBounds(x, y, w, h);
10126         }
10127     },
10128
10129     // private
10130     adjustViewport : function(w, h){
10131         if(!w || !h){
10132             w = Roo.lib.Dom.getViewWidth();
10133             h = Roo.lib.Dom.getViewHeight();
10134         }
10135         // cache the size
10136         this.viewSize = [w, h];
10137         if(this.modal && this.mask.isVisible()){
10138             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
10139             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
10140         }
10141         if(this.isVisible()){
10142             this.constrainXY();
10143         }
10144     },
10145
10146     /**
10147      * Destroys this dialog and all its supporting elements (including any tabs, shim,
10148      * shadow, proxy, mask, etc.)  Also removes all event listeners.
10149      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
10150      */
10151     destroy : function(removeEl){
10152         if(this.isVisible()){
10153             this.animateTarget = null;
10154             this.hide();
10155         }
10156         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
10157         if(this.tabs){
10158             this.tabs.destroy(removeEl);
10159         }
10160         Roo.destroy(
10161              this.shim,
10162              this.proxy,
10163              this.resizer,
10164              this.close,
10165              this.mask
10166         );
10167         if(this.dd){
10168             this.dd.unreg();
10169         }
10170         if(this.buttons){
10171            for(var i = 0, len = this.buttons.length; i < len; i++){
10172                this.buttons[i].destroy();
10173            }
10174         }
10175         this.el.removeAllListeners();
10176         if(removeEl === true){
10177             this.el.update("");
10178             this.el.remove();
10179         }
10180         Roo.DialogManager.unregister(this);
10181     },
10182
10183     // private
10184     startMove : function(){
10185         if(this.proxyDrag){
10186             this.proxy.show();
10187         }
10188         if(this.constraintoviewport !== false){
10189             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
10190         }
10191     },
10192
10193     // private
10194     endMove : function(){
10195         if(!this.proxyDrag){
10196             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
10197         }else{
10198             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
10199             this.proxy.hide();
10200         }
10201         this.refreshSize();
10202         this.adjustAssets();
10203         this.focus();
10204         this.fireEvent("move", this, this.xy[0], this.xy[1]);
10205     },
10206
10207     /**
10208      * Brings this dialog to the front of any other visible dialogs
10209      * @return {Roo.BasicDialog} this
10210      */
10211     toFront : function(){
10212         Roo.DialogManager.bringToFront(this);
10213         return this;
10214     },
10215
10216     /**
10217      * Sends this dialog to the back (under) of any other visible dialogs
10218      * @return {Roo.BasicDialog} this
10219      */
10220     toBack : function(){
10221         Roo.DialogManager.sendToBack(this);
10222         return this;
10223     },
10224
10225     /**
10226      * Centers this dialog in the viewport
10227      * @return {Roo.BasicDialog} this
10228      */
10229     center : function(){
10230         var xy = this.el.getCenterXY(true);
10231         this.moveTo(xy[0], xy[1]);
10232         return this;
10233     },
10234
10235     /**
10236      * Moves the dialog's top-left corner to the specified point
10237      * @param {Number} x
10238      * @param {Number} y
10239      * @return {Roo.BasicDialog} this
10240      */
10241     moveTo : function(x, y){
10242         this.xy = [x,y];
10243         if(this.isVisible()){
10244             this.el.setXY(this.xy);
10245             this.adjustAssets();
10246         }
10247         return this;
10248     },
10249
10250     /**
10251      * Aligns the dialog to the specified element
10252      * @param {String/HTMLElement/Roo.Element} element The element to align to.
10253      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
10254      * @param {Array} offsets (optional) Offset the positioning by [x, y]
10255      * @return {Roo.BasicDialog} this
10256      */
10257     alignTo : function(element, position, offsets){
10258         this.xy = this.el.getAlignToXY(element, position, offsets);
10259         if(this.isVisible()){
10260             this.el.setXY(this.xy);
10261             this.adjustAssets();
10262         }
10263         return this;
10264     },
10265
10266     /**
10267      * Anchors an element to another element and realigns it when the window is resized.
10268      * @param {String/HTMLElement/Roo.Element} element The element to align to.
10269      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
10270      * @param {Array} offsets (optional) Offset the positioning by [x, y]
10271      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
10272      * is a number, it is used as the buffer delay (defaults to 50ms).
10273      * @return {Roo.BasicDialog} this
10274      */
10275     anchorTo : function(el, alignment, offsets, monitorScroll){
10276         var action = function(){
10277             this.alignTo(el, alignment, offsets);
10278         };
10279         Roo.EventManager.onWindowResize(action, this);
10280         var tm = typeof monitorScroll;
10281         if(tm != 'undefined'){
10282             Roo.EventManager.on(window, 'scroll', action, this,
10283                 {buffer: tm == 'number' ? monitorScroll : 50});
10284         }
10285         action.call(this);
10286         return this;
10287     },
10288
10289     /**
10290      * Returns true if the dialog is visible
10291      * @return {Boolean}
10292      */
10293     isVisible : function(){
10294         return this.el.isVisible();
10295     },
10296
10297     // private
10298     animHide : function(callback){
10299         var b = Roo.get(this.animateTarget).getBox();
10300         this.proxy.show();
10301         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
10302         this.el.hide();
10303         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
10304                     this.hideEl.createDelegate(this, [callback]));
10305     },
10306
10307     /**
10308      * Hides the dialog.
10309      * @param {Function} callback (optional) Function to call when the dialog is hidden
10310      * @return {Roo.BasicDialog} this
10311      */
10312     hide : function(callback){
10313         if (this.fireEvent("beforehide", this) === false){
10314             return;
10315         }
10316         if(this.shadow){
10317             this.shadow.hide();
10318         }
10319         if(this.shim) {
10320           this.shim.hide();
10321         }
10322         // sometimes animateTarget seems to get set.. causing problems...
10323         // this just double checks..
10324         if(this.animateTarget && Roo.get(this.animateTarget)) {
10325            this.animHide(callback);
10326         }else{
10327             this.el.hide();
10328             this.hideEl(callback);
10329         }
10330         return this;
10331     },
10332
10333     // private
10334     hideEl : function(callback){
10335         this.proxy.hide();
10336         if(this.modal){
10337             this.mask.hide();
10338             Roo.get(document.body).removeClass("x-body-masked");
10339         }
10340         this.fireEvent("hide", this);
10341         if(typeof callback == "function"){
10342             callback();
10343         }
10344     },
10345
10346     // private
10347     hideAction : function(){
10348         this.setLeft("-10000px");
10349         this.setTop("-10000px");
10350         this.setStyle("visibility", "hidden");
10351     },
10352
10353     // private
10354     refreshSize : function(){
10355         this.size = this.el.getSize();
10356         this.xy = this.el.getXY();
10357         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
10358     },
10359
10360     // private
10361     // z-index is managed by the DialogManager and may be overwritten at any time
10362     setZIndex : function(index){
10363         if(this.modal){
10364             this.mask.setStyle("z-index", index);
10365         }
10366         if(this.shim){
10367             this.shim.setStyle("z-index", ++index);
10368         }
10369         if(this.shadow){
10370             this.shadow.setZIndex(++index);
10371         }
10372         this.el.setStyle("z-index", ++index);
10373         if(this.proxy){
10374             this.proxy.setStyle("z-index", ++index);
10375         }
10376         if(this.resizer){
10377             this.resizer.proxy.setStyle("z-index", ++index);
10378         }
10379
10380         this.lastZIndex = index;
10381     },
10382
10383     /**
10384      * Returns the element for this dialog
10385      * @return {Roo.Element} The underlying dialog Element
10386      */
10387     getEl : function(){
10388         return this.el;
10389     }
10390 });
10391
10392 /**
10393  * @class Roo.DialogManager
10394  * Provides global access to BasicDialogs that have been created and
10395  * support for z-indexing (layering) multiple open dialogs.
10396  */
10397 Roo.DialogManager = function(){
10398     var list = {};
10399     var accessList = [];
10400     var front = null;
10401
10402     // private
10403     var sortDialogs = function(d1, d2){
10404         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
10405     };
10406
10407     // private
10408     var orderDialogs = function(){
10409         accessList.sort(sortDialogs);
10410         var seed = Roo.DialogManager.zseed;
10411         for(var i = 0, len = accessList.length; i < len; i++){
10412             var dlg = accessList[i];
10413             if(dlg){
10414                 dlg.setZIndex(seed + (i*10));
10415             }
10416         }
10417     };
10418
10419     return {
10420         /**
10421          * The starting z-index for BasicDialogs (defaults to 9000)
10422          * @type Number The z-index value
10423          */
10424         zseed : 9000,
10425
10426         // private
10427         register : function(dlg){
10428             list[dlg.id] = dlg;
10429             accessList.push(dlg);
10430         },
10431
10432         // private
10433         unregister : function(dlg){
10434             delete list[dlg.id];
10435             var i=0;
10436             var len=0;
10437             if(!accessList.indexOf){
10438                 for(  i = 0, len = accessList.length; i < len; i++){
10439                     if(accessList[i] == dlg){
10440                         accessList.splice(i, 1);
10441                         return;
10442                     }
10443                 }
10444             }else{
10445                  i = accessList.indexOf(dlg);
10446                 if(i != -1){
10447                     accessList.splice(i, 1);
10448                 }
10449             }
10450         },
10451
10452         /**
10453          * Gets a registered dialog by id
10454          * @param {String/Object} id The id of the dialog or a dialog
10455          * @return {Roo.BasicDialog} this
10456          */
10457         get : function(id){
10458             return typeof id == "object" ? id : list[id];
10459         },
10460
10461         /**
10462          * Brings the specified dialog to the front
10463          * @param {String/Object} dlg The id of the dialog or a dialog
10464          * @return {Roo.BasicDialog} this
10465          */
10466         bringToFront : function(dlg){
10467             dlg = this.get(dlg);
10468             if(dlg != front){
10469                 front = dlg;
10470                 dlg._lastAccess = new Date().getTime();
10471                 orderDialogs();
10472             }
10473             return dlg;
10474         },
10475
10476         /**
10477          * Sends the specified dialog to the back
10478          * @param {String/Object} dlg The id of the dialog or a dialog
10479          * @return {Roo.BasicDialog} this
10480          */
10481         sendToBack : function(dlg){
10482             dlg = this.get(dlg);
10483             dlg._lastAccess = -(new Date().getTime());
10484             orderDialogs();
10485             return dlg;
10486         },
10487
10488         /**
10489          * Hides all dialogs
10490          */
10491         hideAll : function(){
10492             for(var id in list){
10493                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
10494                     list[id].hide();
10495                 }
10496             }
10497         }
10498     };
10499 }();
10500
10501 /**
10502  * @class Roo.LayoutDialog
10503  * @extends Roo.BasicDialog
10504  * Dialog which provides adjustments for working with a layout in a Dialog.
10505  * Add your necessary layout config options to the dialog's config.<br>
10506  * Example usage (including a nested layout):
10507  * <pre><code>
10508 if(!dialog){
10509     dialog = new Roo.LayoutDialog("download-dlg", {
10510         modal: true,
10511         width:600,
10512         height:450,
10513         shadow:true,
10514         minWidth:500,
10515         minHeight:350,
10516         autoTabs:true,
10517         proxyDrag:true,
10518         // layout config merges with the dialog config
10519         center:{
10520             tabPosition: "top",
10521             alwaysShowTabs: true
10522         }
10523     });
10524     dialog.addKeyListener(27, dialog.hide, dialog);
10525     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
10526     dialog.addButton("Build It!", this.getDownload, this);
10527
10528     // we can even add nested layouts
10529     var innerLayout = new Roo.BorderLayout("dl-inner", {
10530         east: {
10531             initialSize: 200,
10532             autoScroll:true,
10533             split:true
10534         },
10535         center: {
10536             autoScroll:true
10537         }
10538     });
10539     innerLayout.beginUpdate();
10540     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
10541     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
10542     innerLayout.endUpdate(true);
10543
10544     var layout = dialog.getLayout();
10545     layout.beginUpdate();
10546     layout.add("center", new Roo.ContentPanel("standard-panel",
10547                         {title: "Download the Source", fitToFrame:true}));
10548     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
10549                {title: "Build your own roo.js"}));
10550     layout.getRegion("center").showPanel(sp);
10551     layout.endUpdate();
10552 }
10553 </code></pre>
10554     * @constructor
10555     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
10556     * @param {Object} config configuration options
10557   */
10558 Roo.LayoutDialog = function(el, cfg){
10559     
10560     var config=  cfg;
10561     if (typeof(cfg) == 'undefined') {
10562         config = Roo.apply({}, el);
10563         // not sure why we use documentElement here.. - it should always be body.
10564         // IE7 borks horribly if we use documentElement.
10565         // webkit also does not like documentElement - it creates a body element...
10566         el = Roo.get( document.body || document.documentElement ).createChild();
10567         //config.autoCreate = true;
10568     }
10569     
10570     
10571     config.autoTabs = false;
10572     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
10573     this.body.setStyle({overflow:"hidden", position:"relative"});
10574     this.layout = new Roo.BorderLayout(this.body.dom, config);
10575     this.layout.monitorWindowResize = false;
10576     this.el.addClass("x-dlg-auto-layout");
10577     // fix case when center region overwrites center function
10578     this.center = Roo.BasicDialog.prototype.center;
10579     this.on("show", this.layout.layout, this.layout, true);
10580     if (config.items) {
10581         var xitems = config.items;
10582         delete config.items;
10583         Roo.each(xitems, this.addxtype, this);
10584     }
10585     
10586     
10587 };
10588 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
10589     /**
10590      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
10591      * @deprecated
10592      */
10593     endUpdate : function(){
10594         this.layout.endUpdate();
10595     },
10596
10597     /**
10598      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
10599      *  @deprecated
10600      */
10601     beginUpdate : function(){
10602         this.layout.beginUpdate();
10603     },
10604
10605     /**
10606      * Get the BorderLayout for this dialog
10607      * @return {Roo.BorderLayout}
10608      */
10609     getLayout : function(){
10610         return this.layout;
10611     },
10612
10613     showEl : function(){
10614         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
10615         if(Roo.isIE7){
10616             this.layout.layout();
10617         }
10618     },
10619
10620     // private
10621     // Use the syncHeightBeforeShow config option to control this automatically
10622     syncBodyHeight : function(){
10623         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
10624         if(this.layout){this.layout.layout();}
10625     },
10626     
10627       /**
10628      * Add an xtype element (actually adds to the layout.)
10629      * @return {Object} xdata xtype object data.
10630      */
10631     
10632     addxtype : function(c) {
10633         return this.layout.addxtype(c);
10634     }
10635 });/*
10636  * Based on:
10637  * Ext JS Library 1.1.1
10638  * Copyright(c) 2006-2007, Ext JS, LLC.
10639  *
10640  * Originally Released Under LGPL - original licence link has changed is not relivant.
10641  *
10642  * Fork - LGPL
10643  * <script type="text/javascript">
10644  */
10645  
10646 /**
10647  * @class Roo.MessageBox
10648  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
10649  * Example usage:
10650  *<pre><code>
10651 // Basic alert:
10652 Roo.Msg.alert('Status', 'Changes saved successfully.');
10653
10654 // Prompt for user data:
10655 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
10656     if (btn == 'ok'){
10657         // process text value...
10658     }
10659 });
10660
10661 // Show a dialog using config options:
10662 Roo.Msg.show({
10663    title:'Save Changes?',
10664    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
10665    buttons: Roo.Msg.YESNOCANCEL,
10666    fn: processResult,
10667    animEl: 'elId'
10668 });
10669 </code></pre>
10670  * @singleton
10671  */
10672 Roo.MessageBox = function(){
10673     var dlg, opt, mask, waitTimer;
10674     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
10675     var buttons, activeTextEl, bwidth;
10676
10677     // private
10678     var handleButton = function(button){
10679         dlg.hide();
10680         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
10681     };
10682
10683     // private
10684     var handleHide = function(){
10685         if(opt && opt.cls){
10686             dlg.el.removeClass(opt.cls);
10687         }
10688         if(waitTimer){
10689             Roo.TaskMgr.stop(waitTimer);
10690             waitTimer = null;
10691         }
10692     };
10693
10694     // private
10695     var updateButtons = function(b){
10696         var width = 0;
10697         if(!b){
10698             buttons["ok"].hide();
10699             buttons["cancel"].hide();
10700             buttons["yes"].hide();
10701             buttons["no"].hide();
10702             dlg.footer.dom.style.display = 'none';
10703             return width;
10704         }
10705         dlg.footer.dom.style.display = '';
10706         for(var k in buttons){
10707             if(typeof buttons[k] != "function"){
10708                 if(b[k]){
10709                     buttons[k].show();
10710                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
10711                     width += buttons[k].el.getWidth()+15;
10712                 }else{
10713                     buttons[k].hide();
10714                 }
10715             }
10716         }
10717         return width;
10718     };
10719
10720     // private
10721     var handleEsc = function(d, k, e){
10722         if(opt && opt.closable !== false){
10723             dlg.hide();
10724         }
10725         if(e){
10726             e.stopEvent();
10727         }
10728     };
10729
10730     return {
10731         /**
10732          * Returns a reference to the underlying {@link Roo.BasicDialog} element
10733          * @return {Roo.BasicDialog} The BasicDialog element
10734          */
10735         getDialog : function(){
10736            if(!dlg){
10737                 dlg = new Roo.BasicDialog("x-msg-box", {
10738                     autoCreate : true,
10739                     shadow: true,
10740                     draggable: true,
10741                     resizable:false,
10742                     constraintoviewport:false,
10743                     fixedcenter:true,
10744                     collapsible : false,
10745                     shim:true,
10746                     modal: true,
10747                     width:400, height:100,
10748                     buttonAlign:"center",
10749                     closeClick : function(){
10750                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
10751                             handleButton("no");
10752                         }else{
10753                             handleButton("cancel");
10754                         }
10755                     }
10756                 });
10757                 dlg.on("hide", handleHide);
10758                 mask = dlg.mask;
10759                 dlg.addKeyListener(27, handleEsc);
10760                 buttons = {};
10761                 var bt = this.buttonText;
10762                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
10763                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
10764                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
10765                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
10766                 bodyEl = dlg.body.createChild({
10767
10768                     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>'
10769                 });
10770                 msgEl = bodyEl.dom.firstChild;
10771                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
10772                 textboxEl.enableDisplayMode();
10773                 textboxEl.addKeyListener([10,13], function(){
10774                     if(dlg.isVisible() && opt && opt.buttons){
10775                         if(opt.buttons.ok){
10776                             handleButton("ok");
10777                         }else if(opt.buttons.yes){
10778                             handleButton("yes");
10779                         }
10780                     }
10781                 });
10782                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
10783                 textareaEl.enableDisplayMode();
10784                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
10785                 progressEl.enableDisplayMode();
10786                 var pf = progressEl.dom.firstChild;
10787                 if (pf) {
10788                     pp = Roo.get(pf.firstChild);
10789                     pp.setHeight(pf.offsetHeight);
10790                 }
10791                 
10792             }
10793             return dlg;
10794         },
10795
10796         /**
10797          * Updates the message box body text
10798          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
10799          * the XHTML-compliant non-breaking space character '&amp;#160;')
10800          * @return {Roo.MessageBox} This message box
10801          */
10802         updateText : function(text){
10803             if(!dlg.isVisible() && !opt.width){
10804                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
10805             }
10806             msgEl.innerHTML = text || '&#160;';
10807       
10808             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
10809             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
10810             var w = Math.max(
10811                     Math.min(opt.width || cw , this.maxWidth), 
10812                     Math.max(opt.minWidth || this.minWidth, bwidth)
10813             );
10814             if(opt.prompt){
10815                 activeTextEl.setWidth(w);
10816             }
10817             if(dlg.isVisible()){
10818                 dlg.fixedcenter = false;
10819             }
10820             // to big, make it scroll. = But as usual stupid IE does not support
10821             // !important..
10822             
10823             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
10824                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
10825                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
10826             } else {
10827                 bodyEl.dom.style.height = '';
10828                 bodyEl.dom.style.overflowY = '';
10829             }
10830             if (cw > w) {
10831                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
10832             } else {
10833                 bodyEl.dom.style.overflowX = '';
10834             }
10835             
10836             dlg.setContentSize(w, bodyEl.getHeight());
10837             if(dlg.isVisible()){
10838                 dlg.fixedcenter = true;
10839             }
10840             return this;
10841         },
10842
10843         /**
10844          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
10845          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
10846          * @param {Number} value Any number between 0 and 1 (e.g., .5)
10847          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
10848          * @return {Roo.MessageBox} This message box
10849          */
10850         updateProgress : function(value, text){
10851             if(text){
10852                 this.updateText(text);
10853             }
10854             if (pp) { // weird bug on my firefox - for some reason this is not defined
10855                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
10856             }
10857             return this;
10858         },        
10859
10860         /**
10861          * Returns true if the message box is currently displayed
10862          * @return {Boolean} True if the message box is visible, else false
10863          */
10864         isVisible : function(){
10865             return dlg && dlg.isVisible();  
10866         },
10867
10868         /**
10869          * Hides the message box if it is displayed
10870          */
10871         hide : function(){
10872             if(this.isVisible()){
10873                 dlg.hide();
10874             }  
10875         },
10876
10877         /**
10878          * Displays a new message box, or reinitializes an existing message box, based on the config options
10879          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
10880          * The following config object properties are supported:
10881          * <pre>
10882 Property    Type             Description
10883 ----------  ---------------  ------------------------------------------------------------------------------------
10884 animEl            String/Element   An id or Element from which the message box should animate as it opens and
10885                                    closes (defaults to undefined)
10886 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
10887                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
10888 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
10889                                    progress and wait dialogs will ignore this property and always hide the
10890                                    close button as they can only be closed programmatically.
10891 cls               String           A custom CSS class to apply to the message box element
10892 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
10893                                    displayed (defaults to 75)
10894 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
10895                                    function will be btn (the name of the button that was clicked, if applicable,
10896                                    e.g. "ok"), and text (the value of the active text field, if applicable).
10897                                    Progress and wait dialogs will ignore this option since they do not respond to
10898                                    user actions and can only be closed programmatically, so any required function
10899                                    should be called by the same code after it closes the dialog.
10900 icon              String           A CSS class that provides a background image to be used as an icon for
10901                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
10902 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
10903 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
10904 modal             Boolean          False to allow user interaction with the page while the message box is
10905                                    displayed (defaults to true)
10906 msg               String           A string that will replace the existing message box body text (defaults
10907                                    to the XHTML-compliant non-breaking space character '&#160;')
10908 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
10909 progress          Boolean          True to display a progress bar (defaults to false)
10910 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
10911 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
10912 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
10913 title             String           The title text
10914 value             String           The string value to set into the active textbox element if displayed
10915 wait              Boolean          True to display a progress bar (defaults to false)
10916 width             Number           The width of the dialog in pixels
10917 </pre>
10918          *
10919          * Example usage:
10920          * <pre><code>
10921 Roo.Msg.show({
10922    title: 'Address',
10923    msg: 'Please enter your address:',
10924    width: 300,
10925    buttons: Roo.MessageBox.OKCANCEL,
10926    multiline: true,
10927    fn: saveAddress,
10928    animEl: 'addAddressBtn'
10929 });
10930 </code></pre>
10931          * @param {Object} config Configuration options
10932          * @return {Roo.MessageBox} This message box
10933          */
10934         show : function(options)
10935         {
10936             
10937             // this causes nightmares if you show one dialog after another
10938             // especially on callbacks..
10939              
10940             if(this.isVisible()){
10941                 
10942                 this.hide();
10943                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
10944                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
10945                 Roo.log("New Dialog Message:" +  options.msg )
10946                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
10947                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
10948                 
10949             }
10950             var d = this.getDialog();
10951             opt = options;
10952             d.setTitle(opt.title || "&#160;");
10953             d.close.setDisplayed(opt.closable !== false);
10954             activeTextEl = textboxEl;
10955             opt.prompt = opt.prompt || (opt.multiline ? true : false);
10956             if(opt.prompt){
10957                 if(opt.multiline){
10958                     textboxEl.hide();
10959                     textareaEl.show();
10960                     textareaEl.setHeight(typeof opt.multiline == "number" ?
10961                         opt.multiline : this.defaultTextHeight);
10962                     activeTextEl = textareaEl;
10963                 }else{
10964                     textboxEl.show();
10965                     textareaEl.hide();
10966                 }
10967             }else{
10968                 textboxEl.hide();
10969                 textareaEl.hide();
10970             }
10971             progressEl.setDisplayed(opt.progress === true);
10972             this.updateProgress(0);
10973             activeTextEl.dom.value = opt.value || "";
10974             if(opt.prompt){
10975                 dlg.setDefaultButton(activeTextEl);
10976             }else{
10977                 var bs = opt.buttons;
10978                 var db = null;
10979                 if(bs && bs.ok){
10980                     db = buttons["ok"];
10981                 }else if(bs && bs.yes){
10982                     db = buttons["yes"];
10983                 }
10984                 dlg.setDefaultButton(db);
10985             }
10986             bwidth = updateButtons(opt.buttons);
10987             this.updateText(opt.msg);
10988             if(opt.cls){
10989                 d.el.addClass(opt.cls);
10990             }
10991             d.proxyDrag = opt.proxyDrag === true;
10992             d.modal = opt.modal !== false;
10993             d.mask = opt.modal !== false ? mask : false;
10994             if(!d.isVisible()){
10995                 // force it to the end of the z-index stack so it gets a cursor in FF
10996                 document.body.appendChild(dlg.el.dom);
10997                 d.animateTarget = null;
10998                 d.show(options.animEl);
10999             }
11000             return this;
11001         },
11002
11003         /**
11004          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
11005          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
11006          * and closing the message box when the process is complete.
11007          * @param {String} title The title bar text
11008          * @param {String} msg The message box body text
11009          * @return {Roo.MessageBox} This message box
11010          */
11011         progress : function(title, msg){
11012             this.show({
11013                 title : title,
11014                 msg : msg,
11015                 buttons: false,
11016                 progress:true,
11017                 closable:false,
11018                 minWidth: this.minProgressWidth,
11019                 modal : true
11020             });
11021             return this;
11022         },
11023
11024         /**
11025          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
11026          * If a callback function is passed it will be called after the user clicks the button, and the
11027          * id of the button that was clicked will be passed as the only parameter to the callback
11028          * (could also be the top-right close button).
11029          * @param {String} title The title bar text
11030          * @param {String} msg The message box body text
11031          * @param {Function} fn (optional) The callback function invoked after the message box is closed
11032          * @param {Object} scope (optional) The scope of the callback function
11033          * @return {Roo.MessageBox} This message box
11034          */
11035         alert : function(title, msg, fn, scope){
11036             this.show({
11037                 title : title,
11038                 msg : msg,
11039                 buttons: this.OK,
11040                 fn: fn,
11041                 scope : scope,
11042                 modal : true
11043             });
11044             return this;
11045         },
11046
11047         /**
11048          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
11049          * interaction while waiting for a long-running process to complete that does not have defined intervals.
11050          * You are responsible for closing the message box when the process is complete.
11051          * @param {String} msg The message box body text
11052          * @param {String} title (optional) The title bar text
11053          * @return {Roo.MessageBox} This message box
11054          */
11055         wait : function(msg, title){
11056             this.show({
11057                 title : title,
11058                 msg : msg,
11059                 buttons: false,
11060                 closable:false,
11061                 progress:true,
11062                 modal:true,
11063                 width:300,
11064                 wait:true
11065             });
11066             waitTimer = Roo.TaskMgr.start({
11067                 run: function(i){
11068                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
11069                 },
11070                 interval: 1000
11071             });
11072             return this;
11073         },
11074
11075         /**
11076          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
11077          * If a callback function is passed it will be called after the user clicks either button, and the id of the
11078          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
11079          * @param {String} title The title bar text
11080          * @param {String} msg The message box body text
11081          * @param {Function} fn (optional) The callback function invoked after the message box is closed
11082          * @param {Object} scope (optional) The scope of the callback function
11083          * @return {Roo.MessageBox} This message box
11084          */
11085         confirm : function(title, msg, fn, scope){
11086             this.show({
11087                 title : title,
11088                 msg : msg,
11089                 buttons: this.YESNO,
11090                 fn: fn,
11091                 scope : scope,
11092                 modal : true
11093             });
11094             return this;
11095         },
11096
11097         /**
11098          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
11099          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
11100          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
11101          * (could also be the top-right close button) and the text that was entered will be passed as the two
11102          * parameters to the callback.
11103          * @param {String} title The title bar text
11104          * @param {String} msg The message box body text
11105          * @param {Function} fn (optional) The callback function invoked after the message box is closed
11106          * @param {Object} scope (optional) The scope of the callback function
11107          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
11108          * property, or the height in pixels to create the textbox (defaults to false / single-line)
11109          * @return {Roo.MessageBox} This message box
11110          */
11111         prompt : function(title, msg, fn, scope, multiline){
11112             this.show({
11113                 title : title,
11114                 msg : msg,
11115                 buttons: this.OKCANCEL,
11116                 fn: fn,
11117                 minWidth:250,
11118                 scope : scope,
11119                 prompt:true,
11120                 multiline: multiline,
11121                 modal : true
11122             });
11123             return this;
11124         },
11125
11126         /**
11127          * Button config that displays a single OK button
11128          * @type Object
11129          */
11130         OK : {ok:true},
11131         /**
11132          * Button config that displays Yes and No buttons
11133          * @type Object
11134          */
11135         YESNO : {yes:true, no:true},
11136         /**
11137          * Button config that displays OK and Cancel buttons
11138          * @type Object
11139          */
11140         OKCANCEL : {ok:true, cancel:true},
11141         /**
11142          * Button config that displays Yes, No and Cancel buttons
11143          * @type Object
11144          */
11145         YESNOCANCEL : {yes:true, no:true, cancel:true},
11146
11147         /**
11148          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
11149          * @type Number
11150          */
11151         defaultTextHeight : 75,
11152         /**
11153          * The maximum width in pixels of the message box (defaults to 600)
11154          * @type Number
11155          */
11156         maxWidth : 600,
11157         /**
11158          * The minimum width in pixels of the message box (defaults to 100)
11159          * @type Number
11160          */
11161         minWidth : 100,
11162         /**
11163          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
11164          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
11165          * @type Number
11166          */
11167         minProgressWidth : 250,
11168         /**
11169          * An object containing the default button text strings that can be overriden for localized language support.
11170          * Supported properties are: ok, cancel, yes and no.
11171          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
11172          * @type Object
11173          */
11174         buttonText : {
11175             ok : "OK",
11176             cancel : "Cancel",
11177             yes : "Yes",
11178             no : "No"
11179         }
11180     };
11181 }();
11182
11183 /**
11184  * Shorthand for {@link Roo.MessageBox}
11185  */
11186 Roo.Msg = Roo.MessageBox;/*
11187  * Based on:
11188  * Ext JS Library 1.1.1
11189  * Copyright(c) 2006-2007, Ext JS, LLC.
11190  *
11191  * Originally Released Under LGPL - original licence link has changed is not relivant.
11192  *
11193  * Fork - LGPL
11194  * <script type="text/javascript">
11195  */
11196 /**
11197  * @class Roo.QuickTips
11198  * Provides attractive and customizable tooltips for any element.
11199  * @singleton
11200  */
11201 Roo.QuickTips = function(){
11202     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
11203     var ce, bd, xy, dd;
11204     var visible = false, disabled = true, inited = false;
11205     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
11206     
11207     var onOver = function(e){
11208         if(disabled){
11209             return;
11210         }
11211         var t = e.getTarget();
11212         if(!t || t.nodeType !== 1 || t == document || t == document.body){
11213             return;
11214         }
11215         if(ce && t == ce.el){
11216             clearTimeout(hideProc);
11217             return;
11218         }
11219         if(t && tagEls[t.id]){
11220             tagEls[t.id].el = t;
11221             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
11222             return;
11223         }
11224         var ttp, et = Roo.fly(t);
11225         var ns = cfg.namespace;
11226         if(tm.interceptTitles && t.title){
11227             ttp = t.title;
11228             t.qtip = ttp;
11229             t.removeAttribute("title");
11230             e.preventDefault();
11231         }else{
11232             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
11233         }
11234         if(ttp){
11235             showProc = show.defer(tm.showDelay, tm, [{
11236                 el: t, 
11237                 text: ttp.replace(/\\n/g,'<br/>'),
11238                 width: et.getAttributeNS(ns, cfg.width),
11239                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
11240                 title: et.getAttributeNS(ns, cfg.title),
11241                     cls: et.getAttributeNS(ns, cfg.cls)
11242             }]);
11243         }
11244     };
11245     
11246     var onOut = function(e){
11247         clearTimeout(showProc);
11248         var t = e.getTarget();
11249         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
11250             hideProc = setTimeout(hide, tm.hideDelay);
11251         }
11252     };
11253     
11254     var onMove = function(e){
11255         if(disabled){
11256             return;
11257         }
11258         xy = e.getXY();
11259         xy[1] += 18;
11260         if(tm.trackMouse && ce){
11261             el.setXY(xy);
11262         }
11263     };
11264     
11265     var onDown = function(e){
11266         clearTimeout(showProc);
11267         clearTimeout(hideProc);
11268         if(!e.within(el)){
11269             if(tm.hideOnClick){
11270                 hide();
11271                 tm.disable();
11272                 tm.enable.defer(100, tm);
11273             }
11274         }
11275     };
11276     
11277     var getPad = function(){
11278         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
11279     };
11280
11281     var show = function(o){
11282         if(disabled){
11283             return;
11284         }
11285         clearTimeout(dismissProc);
11286         ce = o;
11287         if(removeCls){ // in case manually hidden
11288             el.removeClass(removeCls);
11289             removeCls = null;
11290         }
11291         if(ce.cls){
11292             el.addClass(ce.cls);
11293             removeCls = ce.cls;
11294         }
11295         if(ce.title){
11296             tipTitle.update(ce.title);
11297             tipTitle.show();
11298         }else{
11299             tipTitle.update('');
11300             tipTitle.hide();
11301         }
11302         el.dom.style.width  = tm.maxWidth+'px';
11303         //tipBody.dom.style.width = '';
11304         tipBodyText.update(o.text);
11305         var p = getPad(), w = ce.width;
11306         if(!w){
11307             var td = tipBodyText.dom;
11308             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
11309             if(aw > tm.maxWidth){
11310                 w = tm.maxWidth;
11311             }else if(aw < tm.minWidth){
11312                 w = tm.minWidth;
11313             }else{
11314                 w = aw;
11315             }
11316         }
11317         //tipBody.setWidth(w);
11318         el.setWidth(parseInt(w, 10) + p);
11319         if(ce.autoHide === false){
11320             close.setDisplayed(true);
11321             if(dd){
11322                 dd.unlock();
11323             }
11324         }else{
11325             close.setDisplayed(false);
11326             if(dd){
11327                 dd.lock();
11328             }
11329         }
11330         if(xy){
11331             el.avoidY = xy[1]-18;
11332             el.setXY(xy);
11333         }
11334         if(tm.animate){
11335             el.setOpacity(.1);
11336             el.setStyle("visibility", "visible");
11337             el.fadeIn({callback: afterShow});
11338         }else{
11339             afterShow();
11340         }
11341     };
11342     
11343     var afterShow = function(){
11344         if(ce){
11345             el.show();
11346             esc.enable();
11347             if(tm.autoDismiss && ce.autoHide !== false){
11348                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
11349             }
11350         }
11351     };
11352     
11353     var hide = function(noanim){
11354         clearTimeout(dismissProc);
11355         clearTimeout(hideProc);
11356         ce = null;
11357         if(el.isVisible()){
11358             esc.disable();
11359             if(noanim !== true && tm.animate){
11360                 el.fadeOut({callback: afterHide});
11361             }else{
11362                 afterHide();
11363             } 
11364         }
11365     };
11366     
11367     var afterHide = function(){
11368         el.hide();
11369         if(removeCls){
11370             el.removeClass(removeCls);
11371             removeCls = null;
11372         }
11373     };
11374     
11375     return {
11376         /**
11377         * @cfg {Number} minWidth
11378         * The minimum width of the quick tip (defaults to 40)
11379         */
11380        minWidth : 40,
11381         /**
11382         * @cfg {Number} maxWidth
11383         * The maximum width of the quick tip (defaults to 300)
11384         */
11385        maxWidth : 300,
11386         /**
11387         * @cfg {Boolean} interceptTitles
11388         * True to automatically use the element's DOM title value if available (defaults to false)
11389         */
11390        interceptTitles : false,
11391         /**
11392         * @cfg {Boolean} trackMouse
11393         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
11394         */
11395        trackMouse : false,
11396         /**
11397         * @cfg {Boolean} hideOnClick
11398         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
11399         */
11400        hideOnClick : true,
11401         /**
11402         * @cfg {Number} showDelay
11403         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
11404         */
11405        showDelay : 500,
11406         /**
11407         * @cfg {Number} hideDelay
11408         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
11409         */
11410        hideDelay : 200,
11411         /**
11412         * @cfg {Boolean} autoHide
11413         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
11414         * Used in conjunction with hideDelay.
11415         */
11416        autoHide : true,
11417         /**
11418         * @cfg {Boolean}
11419         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
11420         * (defaults to true).  Used in conjunction with autoDismissDelay.
11421         */
11422        autoDismiss : true,
11423         /**
11424         * @cfg {Number}
11425         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
11426         */
11427        autoDismissDelay : 5000,
11428        /**
11429         * @cfg {Boolean} animate
11430         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
11431         */
11432        animate : false,
11433
11434        /**
11435         * @cfg {String} title
11436         * Title text to display (defaults to '').  This can be any valid HTML markup.
11437         */
11438         title: '',
11439        /**
11440         * @cfg {String} text
11441         * Body text to display (defaults to '').  This can be any valid HTML markup.
11442         */
11443         text : '',
11444        /**
11445         * @cfg {String} cls
11446         * A CSS class to apply to the base quick tip element (defaults to '').
11447         */
11448         cls : '',
11449        /**
11450         * @cfg {Number} width
11451         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
11452         * minWidth or maxWidth.
11453         */
11454         width : null,
11455
11456     /**
11457      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
11458      * or display QuickTips in a page.
11459      */
11460        init : function(){
11461           tm = Roo.QuickTips;
11462           cfg = tm.tagConfig;
11463           if(!inited){
11464               if(!Roo.isReady){ // allow calling of init() before onReady
11465                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
11466                   return;
11467               }
11468               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
11469               el.fxDefaults = {stopFx: true};
11470               // maximum custom styling
11471               //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>');
11472               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>');              
11473               tipTitle = el.child('h3');
11474               tipTitle.enableDisplayMode("block");
11475               tipBody = el.child('div.x-tip-bd');
11476               tipBodyText = el.child('div.x-tip-bd-inner');
11477               //bdLeft = el.child('div.x-tip-bd-left');
11478               //bdRight = el.child('div.x-tip-bd-right');
11479               close = el.child('div.x-tip-close');
11480               close.enableDisplayMode("block");
11481               close.on("click", hide);
11482               var d = Roo.get(document);
11483               d.on("mousedown", onDown);
11484               d.on("mouseover", onOver);
11485               d.on("mouseout", onOut);
11486               d.on("mousemove", onMove);
11487               esc = d.addKeyListener(27, hide);
11488               esc.disable();
11489               if(Roo.dd.DD){
11490                   dd = el.initDD("default", null, {
11491                       onDrag : function(){
11492                           el.sync();  
11493                       }
11494                   });
11495                   dd.setHandleElId(tipTitle.id);
11496                   dd.lock();
11497               }
11498               inited = true;
11499           }
11500           this.enable(); 
11501        },
11502
11503     /**
11504      * Configures a new quick tip instance and assigns it to a target element.  The following config options
11505      * are supported:
11506      * <pre>
11507 Property    Type                   Description
11508 ----------  ---------------------  ------------------------------------------------------------------------
11509 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
11510      * </ul>
11511      * @param {Object} config The config object
11512      */
11513        register : function(config){
11514            var cs = config instanceof Array ? config : arguments;
11515            for(var i = 0, len = cs.length; i < len; i++) {
11516                var c = cs[i];
11517                var target = c.target;
11518                if(target){
11519                    if(target instanceof Array){
11520                        for(var j = 0, jlen = target.length; j < jlen; j++){
11521                            tagEls[target[j]] = c;
11522                        }
11523                    }else{
11524                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
11525                    }
11526                }
11527            }
11528        },
11529
11530     /**
11531      * Removes this quick tip from its element and destroys it.
11532      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
11533      */
11534        unregister : function(el){
11535            delete tagEls[Roo.id(el)];
11536        },
11537
11538     /**
11539      * Enable this quick tip.
11540      */
11541        enable : function(){
11542            if(inited && disabled){
11543                locks.pop();
11544                if(locks.length < 1){
11545                    disabled = false;
11546                }
11547            }
11548        },
11549
11550     /**
11551      * Disable this quick tip.
11552      */
11553        disable : function(){
11554           disabled = true;
11555           clearTimeout(showProc);
11556           clearTimeout(hideProc);
11557           clearTimeout(dismissProc);
11558           if(ce){
11559               hide(true);
11560           }
11561           locks.push(1);
11562        },
11563
11564     /**
11565      * Returns true if the quick tip is enabled, else false.
11566      */
11567        isEnabled : function(){
11568             return !disabled;
11569        },
11570
11571         // private
11572        tagConfig : {
11573            namespace : "roo", // was ext?? this may break..
11574            alt_namespace : "ext",
11575            attribute : "qtip",
11576            width : "width",
11577            target : "target",
11578            title : "qtitle",
11579            hide : "hide",
11580            cls : "qclass"
11581        }
11582    };
11583 }();
11584
11585 // backwards compat
11586 Roo.QuickTips.tips = Roo.QuickTips.register;/*
11587  * Based on:
11588  * Ext JS Library 1.1.1
11589  * Copyright(c) 2006-2007, Ext JS, LLC.
11590  *
11591  * Originally Released Under LGPL - original licence link has changed is not relivant.
11592  *
11593  * Fork - LGPL
11594  * <script type="text/javascript">
11595  */
11596  
11597
11598 /**
11599  * @class Roo.tree.TreePanel
11600  * @extends Roo.data.Tree
11601
11602  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
11603  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
11604  * @cfg {Boolean} enableDD true to enable drag and drop
11605  * @cfg {Boolean} enableDrag true to enable just drag
11606  * @cfg {Boolean} enableDrop true to enable just drop
11607  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
11608  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
11609  * @cfg {String} ddGroup The DD group this TreePanel belongs to
11610  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
11611  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
11612  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
11613  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
11614  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
11615  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
11616  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
11617  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
11618  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
11619  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
11620  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
11621  * @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>
11622  * @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>
11623  * 
11624  * @constructor
11625  * @param {String/HTMLElement/Element} el The container element
11626  * @param {Object} config
11627  */
11628 Roo.tree.TreePanel = function(el, config){
11629     var root = false;
11630     var loader = false;
11631     if (config.root) {
11632         root = config.root;
11633         delete config.root;
11634     }
11635     if (config.loader) {
11636         loader = config.loader;
11637         delete config.loader;
11638     }
11639     
11640     Roo.apply(this, config);
11641     Roo.tree.TreePanel.superclass.constructor.call(this);
11642     this.el = Roo.get(el);
11643     this.el.addClass('x-tree');
11644     //console.log(root);
11645     if (root) {
11646         this.setRootNode( Roo.factory(root, Roo.tree));
11647     }
11648     if (loader) {
11649         this.loader = Roo.factory(loader, Roo.tree);
11650     }
11651    /**
11652     * Read-only. The id of the container element becomes this TreePanel's id.
11653     */
11654     this.id = this.el.id;
11655     this.addEvents({
11656         /**
11657         * @event beforeload
11658         * Fires before a node is loaded, return false to cancel
11659         * @param {Node} node The node being loaded
11660         */
11661         "beforeload" : true,
11662         /**
11663         * @event load
11664         * Fires when a node is loaded
11665         * @param {Node} node The node that was loaded
11666         */
11667         "load" : true,
11668         /**
11669         * @event textchange
11670         * Fires when the text for a node is changed
11671         * @param {Node} node The node
11672         * @param {String} text The new text
11673         * @param {String} oldText The old text
11674         */
11675         "textchange" : true,
11676         /**
11677         * @event beforeexpand
11678         * Fires before a node is expanded, return false to cancel.
11679         * @param {Node} node The node
11680         * @param {Boolean} deep
11681         * @param {Boolean} anim
11682         */
11683         "beforeexpand" : true,
11684         /**
11685         * @event beforecollapse
11686         * Fires before a node is collapsed, return false to cancel.
11687         * @param {Node} node The node
11688         * @param {Boolean} deep
11689         * @param {Boolean} anim
11690         */
11691         "beforecollapse" : true,
11692         /**
11693         * @event expand
11694         * Fires when a node is expanded
11695         * @param {Node} node The node
11696         */
11697         "expand" : true,
11698         /**
11699         * @event disabledchange
11700         * Fires when the disabled status of a node changes
11701         * @param {Node} node The node
11702         * @param {Boolean} disabled
11703         */
11704         "disabledchange" : true,
11705         /**
11706         * @event collapse
11707         * Fires when a node is collapsed
11708         * @param {Node} node The node
11709         */
11710         "collapse" : true,
11711         /**
11712         * @event beforeclick
11713         * Fires before click processing on a node. Return false to cancel the default action.
11714         * @param {Node} node The node
11715         * @param {Roo.EventObject} e The event object
11716         */
11717         "beforeclick":true,
11718         /**
11719         * @event checkchange
11720         * Fires when a node with a checkbox's checked property changes
11721         * @param {Node} this This node
11722         * @param {Boolean} checked
11723         */
11724         "checkchange":true,
11725         /**
11726         * @event click
11727         * Fires when a node is clicked
11728         * @param {Node} node The node
11729         * @param {Roo.EventObject} e The event object
11730         */
11731         "click":true,
11732         /**
11733         * @event dblclick
11734         * Fires when a node is double clicked
11735         * @param {Node} node The node
11736         * @param {Roo.EventObject} e The event object
11737         */
11738         "dblclick":true,
11739         /**
11740         * @event contextmenu
11741         * Fires when a node is right clicked
11742         * @param {Node} node The node
11743         * @param {Roo.EventObject} e The event object
11744         */
11745         "contextmenu":true,
11746         /**
11747         * @event beforechildrenrendered
11748         * Fires right before the child nodes for a node are rendered
11749         * @param {Node} node The node
11750         */
11751         "beforechildrenrendered":true,
11752         /**
11753         * @event startdrag
11754         * Fires when a node starts being dragged
11755         * @param {Roo.tree.TreePanel} this
11756         * @param {Roo.tree.TreeNode} node
11757         * @param {event} e The raw browser event
11758         */ 
11759        "startdrag" : true,
11760        /**
11761         * @event enddrag
11762         * Fires when a drag operation is complete
11763         * @param {Roo.tree.TreePanel} this
11764         * @param {Roo.tree.TreeNode} node
11765         * @param {event} e The raw browser event
11766         */
11767        "enddrag" : true,
11768        /**
11769         * @event dragdrop
11770         * Fires when a dragged node is dropped on a valid DD target
11771         * @param {Roo.tree.TreePanel} this
11772         * @param {Roo.tree.TreeNode} node
11773         * @param {DD} dd The dd it was dropped on
11774         * @param {event} e The raw browser event
11775         */
11776        "dragdrop" : true,
11777        /**
11778         * @event beforenodedrop
11779         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
11780         * passed to handlers has the following properties:<br />
11781         * <ul style="padding:5px;padding-left:16px;">
11782         * <li>tree - The TreePanel</li>
11783         * <li>target - The node being targeted for the drop</li>
11784         * <li>data - The drag data from the drag source</li>
11785         * <li>point - The point of the drop - append, above or below</li>
11786         * <li>source - The drag source</li>
11787         * <li>rawEvent - Raw mouse event</li>
11788         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
11789         * to be inserted by setting them on this object.</li>
11790         * <li>cancel - Set this to true to cancel the drop.</li>
11791         * </ul>
11792         * @param {Object} dropEvent
11793         */
11794        "beforenodedrop" : true,
11795        /**
11796         * @event nodedrop
11797         * Fires after a DD object is dropped on a node in this tree. The dropEvent
11798         * passed to handlers has the following properties:<br />
11799         * <ul style="padding:5px;padding-left:16px;">
11800         * <li>tree - The TreePanel</li>
11801         * <li>target - The node being targeted for the drop</li>
11802         * <li>data - The drag data from the drag source</li>
11803         * <li>point - The point of the drop - append, above or below</li>
11804         * <li>source - The drag source</li>
11805         * <li>rawEvent - Raw mouse event</li>
11806         * <li>dropNode - Dropped node(s).</li>
11807         * </ul>
11808         * @param {Object} dropEvent
11809         */
11810        "nodedrop" : true,
11811         /**
11812         * @event nodedragover
11813         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
11814         * passed to handlers has the following properties:<br />
11815         * <ul style="padding:5px;padding-left:16px;">
11816         * <li>tree - The TreePanel</li>
11817         * <li>target - The node being targeted for the drop</li>
11818         * <li>data - The drag data from the drag source</li>
11819         * <li>point - The point of the drop - append, above or below</li>
11820         * <li>source - The drag source</li>
11821         * <li>rawEvent - Raw mouse event</li>
11822         * <li>dropNode - Drop node(s) provided by the source.</li>
11823         * <li>cancel - Set this to true to signal drop not allowed.</li>
11824         * </ul>
11825         * @param {Object} dragOverEvent
11826         */
11827        "nodedragover" : true
11828         
11829     });
11830     if(this.singleExpand){
11831        this.on("beforeexpand", this.restrictExpand, this);
11832     }
11833     if (this.editor) {
11834         this.editor.tree = this;
11835         this.editor = Roo.factory(this.editor, Roo.tree);
11836     }
11837     
11838     if (this.selModel) {
11839         this.selModel = Roo.factory(this.selModel, Roo.tree);
11840     }
11841    
11842 };
11843 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
11844     rootVisible : true,
11845     animate: Roo.enableFx,
11846     lines : true,
11847     enableDD : false,
11848     hlDrop : Roo.enableFx,
11849   
11850     renderer: false,
11851     
11852     rendererTip: false,
11853     // private
11854     restrictExpand : function(node){
11855         var p = node.parentNode;
11856         if(p){
11857             if(p.expandedChild && p.expandedChild.parentNode == p){
11858                 p.expandedChild.collapse();
11859             }
11860             p.expandedChild = node;
11861         }
11862     },
11863
11864     // private override
11865     setRootNode : function(node){
11866         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
11867         if(!this.rootVisible){
11868             node.ui = new Roo.tree.RootTreeNodeUI(node);
11869         }
11870         return node;
11871     },
11872
11873     /**
11874      * Returns the container element for this TreePanel
11875      */
11876     getEl : function(){
11877         return this.el;
11878     },
11879
11880     /**
11881      * Returns the default TreeLoader for this TreePanel
11882      */
11883     getLoader : function(){
11884         return this.loader;
11885     },
11886
11887     /**
11888      * Expand all nodes
11889      */
11890     expandAll : function(){
11891         this.root.expand(true);
11892     },
11893
11894     /**
11895      * Collapse all nodes
11896      */
11897     collapseAll : function(){
11898         this.root.collapse(true);
11899     },
11900
11901     /**
11902      * Returns the selection model used by this TreePanel
11903      */
11904     getSelectionModel : function(){
11905         if(!this.selModel){
11906             this.selModel = new Roo.tree.DefaultSelectionModel();
11907         }
11908         return this.selModel;
11909     },
11910
11911     /**
11912      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
11913      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
11914      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
11915      * @return {Array}
11916      */
11917     getChecked : function(a, startNode){
11918         startNode = startNode || this.root;
11919         var r = [];
11920         var f = function(){
11921             if(this.attributes.checked){
11922                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
11923             }
11924         }
11925         startNode.cascade(f);
11926         return r;
11927     },
11928
11929     /**
11930      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11931      * @param {String} path
11932      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11933      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
11934      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
11935      */
11936     expandPath : function(path, attr, callback){
11937         attr = attr || "id";
11938         var keys = path.split(this.pathSeparator);
11939         var curNode = this.root;
11940         if(curNode.attributes[attr] != keys[1]){ // invalid root
11941             if(callback){
11942                 callback(false, null);
11943             }
11944             return;
11945         }
11946         var index = 1;
11947         var f = function(){
11948             if(++index == keys.length){
11949                 if(callback){
11950                     callback(true, curNode);
11951                 }
11952                 return;
11953             }
11954             var c = curNode.findChild(attr, keys[index]);
11955             if(!c){
11956                 if(callback){
11957                     callback(false, curNode);
11958                 }
11959                 return;
11960             }
11961             curNode = c;
11962             c.expand(false, false, f);
11963         };
11964         curNode.expand(false, false, f);
11965     },
11966
11967     /**
11968      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11969      * @param {String} path
11970      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11971      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
11972      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
11973      */
11974     selectPath : function(path, attr, callback){
11975         attr = attr || "id";
11976         var keys = path.split(this.pathSeparator);
11977         var v = keys.pop();
11978         if(keys.length > 0){
11979             var f = function(success, node){
11980                 if(success && node){
11981                     var n = node.findChild(attr, v);
11982                     if(n){
11983                         n.select();
11984                         if(callback){
11985                             callback(true, n);
11986                         }
11987                     }else if(callback){
11988                         callback(false, n);
11989                     }
11990                 }else{
11991                     if(callback){
11992                         callback(false, n);
11993                     }
11994                 }
11995             };
11996             this.expandPath(keys.join(this.pathSeparator), attr, f);
11997         }else{
11998             this.root.select();
11999             if(callback){
12000                 callback(true, this.root);
12001             }
12002         }
12003     },
12004
12005     getTreeEl : function(){
12006         return this.el;
12007     },
12008
12009     /**
12010      * Trigger rendering of this TreePanel
12011      */
12012     render : function(){
12013         if (this.innerCt) {
12014             return this; // stop it rendering more than once!!
12015         }
12016         
12017         this.innerCt = this.el.createChild({tag:"ul",
12018                cls:"x-tree-root-ct " +
12019                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
12020
12021         if(this.containerScroll){
12022             Roo.dd.ScrollManager.register(this.el);
12023         }
12024         if((this.enableDD || this.enableDrop) && !this.dropZone){
12025            /**
12026             * The dropZone used by this tree if drop is enabled
12027             * @type Roo.tree.TreeDropZone
12028             */
12029              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
12030                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
12031            });
12032         }
12033         if((this.enableDD || this.enableDrag) && !this.dragZone){
12034            /**
12035             * The dragZone used by this tree if drag is enabled
12036             * @type Roo.tree.TreeDragZone
12037             */
12038             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
12039                ddGroup: this.ddGroup || "TreeDD",
12040                scroll: this.ddScroll
12041            });
12042         }
12043         this.getSelectionModel().init(this);
12044         if (!this.root) {
12045             Roo.log("ROOT not set in tree");
12046             return this;
12047         }
12048         this.root.render();
12049         if(!this.rootVisible){
12050             this.root.renderChildren();
12051         }
12052         return this;
12053     }
12054 });/*
12055  * Based on:
12056  * Ext JS Library 1.1.1
12057  * Copyright(c) 2006-2007, Ext JS, LLC.
12058  *
12059  * Originally Released Under LGPL - original licence link has changed is not relivant.
12060  *
12061  * Fork - LGPL
12062  * <script type="text/javascript">
12063  */
12064  
12065
12066 /**
12067  * @class Roo.tree.DefaultSelectionModel
12068  * @extends Roo.util.Observable
12069  * The default single selection for a TreePanel.
12070  * @param {Object} cfg Configuration
12071  */
12072 Roo.tree.DefaultSelectionModel = function(cfg){
12073    this.selNode = null;
12074    
12075    
12076    
12077    this.addEvents({
12078        /**
12079         * @event selectionchange
12080         * Fires when the selected node changes
12081         * @param {DefaultSelectionModel} this
12082         * @param {TreeNode} node the new selection
12083         */
12084        "selectionchange" : true,
12085
12086        /**
12087         * @event beforeselect
12088         * Fires before the selected node changes, return false to cancel the change
12089         * @param {DefaultSelectionModel} this
12090         * @param {TreeNode} node the new selection
12091         * @param {TreeNode} node the old selection
12092         */
12093        "beforeselect" : true
12094    });
12095    
12096     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
12097 };
12098
12099 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
12100     init : function(tree){
12101         this.tree = tree;
12102         tree.getTreeEl().on("keydown", this.onKeyDown, this);
12103         tree.on("click", this.onNodeClick, this);
12104     },
12105     
12106     onNodeClick : function(node, e){
12107         if (e.ctrlKey && this.selNode == node)  {
12108             this.unselect(node);
12109             return;
12110         }
12111         this.select(node);
12112     },
12113     
12114     /**
12115      * Select a node.
12116      * @param {TreeNode} node The node to select
12117      * @return {TreeNode} The selected node
12118      */
12119     select : function(node){
12120         var last = this.selNode;
12121         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
12122             if(last){
12123                 last.ui.onSelectedChange(false);
12124             }
12125             this.selNode = node;
12126             node.ui.onSelectedChange(true);
12127             this.fireEvent("selectionchange", this, node, last);
12128         }
12129         return node;
12130     },
12131     
12132     /**
12133      * Deselect a node.
12134      * @param {TreeNode} node The node to unselect
12135      */
12136     unselect : function(node){
12137         if(this.selNode == node){
12138             this.clearSelections();
12139         }    
12140     },
12141     
12142     /**
12143      * Clear all selections
12144      */
12145     clearSelections : function(){
12146         var n = this.selNode;
12147         if(n){
12148             n.ui.onSelectedChange(false);
12149             this.selNode = null;
12150             this.fireEvent("selectionchange", this, null);
12151         }
12152         return n;
12153     },
12154     
12155     /**
12156      * Get the selected node
12157      * @return {TreeNode} The selected node
12158      */
12159     getSelectedNode : function(){
12160         return this.selNode;    
12161     },
12162     
12163     /**
12164      * Returns true if the node is selected
12165      * @param {TreeNode} node The node to check
12166      * @return {Boolean}
12167      */
12168     isSelected : function(node){
12169         return this.selNode == node;  
12170     },
12171
12172     /**
12173      * Selects the node above the selected node in the tree, intelligently walking the nodes
12174      * @return TreeNode The new selection
12175      */
12176     selectPrevious : function(){
12177         var s = this.selNode || this.lastSelNode;
12178         if(!s){
12179             return null;
12180         }
12181         var ps = s.previousSibling;
12182         if(ps){
12183             if(!ps.isExpanded() || ps.childNodes.length < 1){
12184                 return this.select(ps);
12185             } else{
12186                 var lc = ps.lastChild;
12187                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
12188                     lc = lc.lastChild;
12189                 }
12190                 return this.select(lc);
12191             }
12192         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
12193             return this.select(s.parentNode);
12194         }
12195         return null;
12196     },
12197
12198     /**
12199      * Selects the node above the selected node in the tree, intelligently walking the nodes
12200      * @return TreeNode The new selection
12201      */
12202     selectNext : function(){
12203         var s = this.selNode || this.lastSelNode;
12204         if(!s){
12205             return null;
12206         }
12207         if(s.firstChild && s.isExpanded()){
12208              return this.select(s.firstChild);
12209          }else if(s.nextSibling){
12210              return this.select(s.nextSibling);
12211          }else if(s.parentNode){
12212             var newS = null;
12213             s.parentNode.bubble(function(){
12214                 if(this.nextSibling){
12215                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
12216                     return false;
12217                 }
12218             });
12219             return newS;
12220          }
12221         return null;
12222     },
12223
12224     onKeyDown : function(e){
12225         var s = this.selNode || this.lastSelNode;
12226         // undesirable, but required
12227         var sm = this;
12228         if(!s){
12229             return;
12230         }
12231         var k = e.getKey();
12232         switch(k){
12233              case e.DOWN:
12234                  e.stopEvent();
12235                  this.selectNext();
12236              break;
12237              case e.UP:
12238                  e.stopEvent();
12239                  this.selectPrevious();
12240              break;
12241              case e.RIGHT:
12242                  e.preventDefault();
12243                  if(s.hasChildNodes()){
12244                      if(!s.isExpanded()){
12245                          s.expand();
12246                      }else if(s.firstChild){
12247                          this.select(s.firstChild, e);
12248                      }
12249                  }
12250              break;
12251              case e.LEFT:
12252                  e.preventDefault();
12253                  if(s.hasChildNodes() && s.isExpanded()){
12254                      s.collapse();
12255                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
12256                      this.select(s.parentNode, e);
12257                  }
12258              break;
12259         };
12260     }
12261 });
12262
12263 /**
12264  * @class Roo.tree.MultiSelectionModel
12265  * @extends Roo.util.Observable
12266  * Multi selection for a TreePanel.
12267  * @param {Object} cfg Configuration
12268  */
12269 Roo.tree.MultiSelectionModel = function(){
12270    this.selNodes = [];
12271    this.selMap = {};
12272    this.addEvents({
12273        /**
12274         * @event selectionchange
12275         * Fires when the selected nodes change
12276         * @param {MultiSelectionModel} this
12277         * @param {Array} nodes Array of the selected nodes
12278         */
12279        "selectionchange" : true
12280    });
12281    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
12282    
12283 };
12284
12285 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
12286     init : function(tree){
12287         this.tree = tree;
12288         tree.getTreeEl().on("keydown", this.onKeyDown, this);
12289         tree.on("click", this.onNodeClick, this);
12290     },
12291     
12292     onNodeClick : function(node, e){
12293         this.select(node, e, e.ctrlKey);
12294     },
12295     
12296     /**
12297      * Select a node.
12298      * @param {TreeNode} node The node to select
12299      * @param {EventObject} e (optional) An event associated with the selection
12300      * @param {Boolean} keepExisting True to retain existing selections
12301      * @return {TreeNode} The selected node
12302      */
12303     select : function(node, e, keepExisting){
12304         if(keepExisting !== true){
12305             this.clearSelections(true);
12306         }
12307         if(this.isSelected(node)){
12308             this.lastSelNode = node;
12309             return node;
12310         }
12311         this.selNodes.push(node);
12312         this.selMap[node.id] = node;
12313         this.lastSelNode = node;
12314         node.ui.onSelectedChange(true);
12315         this.fireEvent("selectionchange", this, this.selNodes);
12316         return node;
12317     },
12318     
12319     /**
12320      * Deselect a node.
12321      * @param {TreeNode} node The node to unselect
12322      */
12323     unselect : function(node){
12324         if(this.selMap[node.id]){
12325             node.ui.onSelectedChange(false);
12326             var sn = this.selNodes;
12327             var index = -1;
12328             if(sn.indexOf){
12329                 index = sn.indexOf(node);
12330             }else{
12331                 for(var i = 0, len = sn.length; i < len; i++){
12332                     if(sn[i] == node){
12333                         index = i;
12334                         break;
12335                     }
12336                 }
12337             }
12338             if(index != -1){
12339                 this.selNodes.splice(index, 1);
12340             }
12341             delete this.selMap[node.id];
12342             this.fireEvent("selectionchange", this, this.selNodes);
12343         }
12344     },
12345     
12346     /**
12347      * Clear all selections
12348      */
12349     clearSelections : function(suppressEvent){
12350         var sn = this.selNodes;
12351         if(sn.length > 0){
12352             for(var i = 0, len = sn.length; i < len; i++){
12353                 sn[i].ui.onSelectedChange(false);
12354             }
12355             this.selNodes = [];
12356             this.selMap = {};
12357             if(suppressEvent !== true){
12358                 this.fireEvent("selectionchange", this, this.selNodes);
12359             }
12360         }
12361     },
12362     
12363     /**
12364      * Returns true if the node is selected
12365      * @param {TreeNode} node The node to check
12366      * @return {Boolean}
12367      */
12368     isSelected : function(node){
12369         return this.selMap[node.id] ? true : false;  
12370     },
12371     
12372     /**
12373      * Returns an array of the selected nodes
12374      * @return {Array}
12375      */
12376     getSelectedNodes : function(){
12377         return this.selNodes;    
12378     },
12379
12380     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
12381
12382     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
12383
12384     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
12385 });/*
12386  * Based on:
12387  * Ext JS Library 1.1.1
12388  * Copyright(c) 2006-2007, Ext JS, LLC.
12389  *
12390  * Originally Released Under LGPL - original licence link has changed is not relivant.
12391  *
12392  * Fork - LGPL
12393  * <script type="text/javascript">
12394  */
12395  
12396 /**
12397  * @class Roo.tree.TreeNode
12398  * @extends Roo.data.Node
12399  * @cfg {String} text The text for this node
12400  * @cfg {Boolean} expanded true to start the node expanded
12401  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
12402  * @cfg {Boolean} allowDrop false if this node cannot be drop on
12403  * @cfg {Boolean} disabled true to start the node disabled
12404  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
12405  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
12406  * @cfg {String} cls A css class to be added to the node
12407  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
12408  * @cfg {String} href URL of the link used for the node (defaults to #)
12409  * @cfg {String} hrefTarget target frame for the link
12410  * @cfg {String} qtip An Ext QuickTip for the node
12411  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
12412  * @cfg {Boolean} singleClickExpand True for single click expand on this node
12413  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
12414  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
12415  * (defaults to undefined with no checkbox rendered)
12416  * @constructor
12417  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
12418  */
12419 Roo.tree.TreeNode = function(attributes){
12420     attributes = attributes || {};
12421     if(typeof attributes == "string"){
12422         attributes = {text: attributes};
12423     }
12424     this.childrenRendered = false;
12425     this.rendered = false;
12426     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
12427     this.expanded = attributes.expanded === true;
12428     this.isTarget = attributes.isTarget !== false;
12429     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
12430     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
12431
12432     /**
12433      * Read-only. The text for this node. To change it use setText().
12434      * @type String
12435      */
12436     this.text = attributes.text;
12437     /**
12438      * True if this node is disabled.
12439      * @type Boolean
12440      */
12441     this.disabled = attributes.disabled === true;
12442
12443     this.addEvents({
12444         /**
12445         * @event textchange
12446         * Fires when the text for this node is changed
12447         * @param {Node} this This node
12448         * @param {String} text The new text
12449         * @param {String} oldText The old text
12450         */
12451         "textchange" : true,
12452         /**
12453         * @event beforeexpand
12454         * Fires before this node is expanded, return false to cancel.
12455         * @param {Node} this This node
12456         * @param {Boolean} deep
12457         * @param {Boolean} anim
12458         */
12459         "beforeexpand" : true,
12460         /**
12461         * @event beforecollapse
12462         * Fires before this node is collapsed, return false to cancel.
12463         * @param {Node} this This node
12464         * @param {Boolean} deep
12465         * @param {Boolean} anim
12466         */
12467         "beforecollapse" : true,
12468         /**
12469         * @event expand
12470         * Fires when this node is expanded
12471         * @param {Node} this This node
12472         */
12473         "expand" : true,
12474         /**
12475         * @event disabledchange
12476         * Fires when the disabled status of this node changes
12477         * @param {Node} this This node
12478         * @param {Boolean} disabled
12479         */
12480         "disabledchange" : true,
12481         /**
12482         * @event collapse
12483         * Fires when this node is collapsed
12484         * @param {Node} this This node
12485         */
12486         "collapse" : true,
12487         /**
12488         * @event beforeclick
12489         * Fires before click processing. Return false to cancel the default action.
12490         * @param {Node} this This node
12491         * @param {Roo.EventObject} e The event object
12492         */
12493         "beforeclick":true,
12494         /**
12495         * @event checkchange
12496         * Fires when a node with a checkbox's checked property changes
12497         * @param {Node} this This node
12498         * @param {Boolean} checked
12499         */
12500         "checkchange":true,
12501         /**
12502         * @event click
12503         * Fires when this node is clicked
12504         * @param {Node} this This node
12505         * @param {Roo.EventObject} e The event object
12506         */
12507         "click":true,
12508         /**
12509         * @event dblclick
12510         * Fires when this node is double clicked
12511         * @param {Node} this This node
12512         * @param {Roo.EventObject} e The event object
12513         */
12514         "dblclick":true,
12515         /**
12516         * @event contextmenu
12517         * Fires when this node is right clicked
12518         * @param {Node} this This node
12519         * @param {Roo.EventObject} e The event object
12520         */
12521         "contextmenu":true,
12522         /**
12523         * @event beforechildrenrendered
12524         * Fires right before the child nodes for this node are rendered
12525         * @param {Node} this This node
12526         */
12527         "beforechildrenrendered":true
12528     });
12529
12530     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
12531
12532     /**
12533      * Read-only. The UI for this node
12534      * @type TreeNodeUI
12535      */
12536     this.ui = new uiClass(this);
12537     
12538     // finally support items[]
12539     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
12540         return;
12541     }
12542     
12543     
12544     Roo.each(this.attributes.items, function(c) {
12545         this.appendChild(Roo.factory(c,Roo.Tree));
12546     }, this);
12547     delete this.attributes.items;
12548     
12549     
12550     
12551 };
12552 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
12553     preventHScroll: true,
12554     /**
12555      * Returns true if this node is expanded
12556      * @return {Boolean}
12557      */
12558     isExpanded : function(){
12559         return this.expanded;
12560     },
12561
12562     /**
12563      * Returns the UI object for this node
12564      * @return {TreeNodeUI}
12565      */
12566     getUI : function(){
12567         return this.ui;
12568     },
12569
12570     // private override
12571     setFirstChild : function(node){
12572         var of = this.firstChild;
12573         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
12574         if(this.childrenRendered && of && node != of){
12575             of.renderIndent(true, true);
12576         }
12577         if(this.rendered){
12578             this.renderIndent(true, true);
12579         }
12580     },
12581
12582     // private override
12583     setLastChild : function(node){
12584         var ol = this.lastChild;
12585         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
12586         if(this.childrenRendered && ol && node != ol){
12587             ol.renderIndent(true, true);
12588         }
12589         if(this.rendered){
12590             this.renderIndent(true, true);
12591         }
12592     },
12593
12594     // these methods are overridden to provide lazy rendering support
12595     // private override
12596     appendChild : function()
12597     {
12598         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
12599         if(node && this.childrenRendered){
12600             node.render();
12601         }
12602         this.ui.updateExpandIcon();
12603         return node;
12604     },
12605
12606     // private override
12607     removeChild : function(node){
12608         this.ownerTree.getSelectionModel().unselect(node);
12609         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
12610         // if it's been rendered remove dom node
12611         if(this.childrenRendered){
12612             node.ui.remove();
12613         }
12614         if(this.childNodes.length < 1){
12615             this.collapse(false, false);
12616         }else{
12617             this.ui.updateExpandIcon();
12618         }
12619         if(!this.firstChild) {
12620             this.childrenRendered = false;
12621         }
12622         return node;
12623     },
12624
12625     // private override
12626     insertBefore : function(node, refNode){
12627         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
12628         if(newNode && refNode && this.childrenRendered){
12629             node.render();
12630         }
12631         this.ui.updateExpandIcon();
12632         return newNode;
12633     },
12634
12635     /**
12636      * Sets the text for this node
12637      * @param {String} text
12638      */
12639     setText : function(text){
12640         var oldText = this.text;
12641         this.text = text;
12642         this.attributes.text = text;
12643         if(this.rendered){ // event without subscribing
12644             this.ui.onTextChange(this, text, oldText);
12645         }
12646         this.fireEvent("textchange", this, text, oldText);
12647     },
12648
12649     /**
12650      * Triggers selection of this node
12651      */
12652     select : function(){
12653         this.getOwnerTree().getSelectionModel().select(this);
12654     },
12655
12656     /**
12657      * Triggers deselection of this node
12658      */
12659     unselect : function(){
12660         this.getOwnerTree().getSelectionModel().unselect(this);
12661     },
12662
12663     /**
12664      * Returns true if this node is selected
12665      * @return {Boolean}
12666      */
12667     isSelected : function(){
12668         return this.getOwnerTree().getSelectionModel().isSelected(this);
12669     },
12670
12671     /**
12672      * Expand this node.
12673      * @param {Boolean} deep (optional) True to expand all children as well
12674      * @param {Boolean} anim (optional) false to cancel the default animation
12675      * @param {Function} callback (optional) A callback to be called when
12676      * expanding this node completes (does not wait for deep expand to complete).
12677      * Called with 1 parameter, this node.
12678      */
12679     expand : function(deep, anim, callback){
12680         if(!this.expanded){
12681             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
12682                 return;
12683             }
12684             if(!this.childrenRendered){
12685                 this.renderChildren();
12686             }
12687             this.expanded = true;
12688             
12689             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
12690                 this.ui.animExpand(function(){
12691                     this.fireEvent("expand", this);
12692                     if(typeof callback == "function"){
12693                         callback(this);
12694                     }
12695                     if(deep === true){
12696                         this.expandChildNodes(true);
12697                     }
12698                 }.createDelegate(this));
12699                 return;
12700             }else{
12701                 this.ui.expand();
12702                 this.fireEvent("expand", this);
12703                 if(typeof callback == "function"){
12704                     callback(this);
12705                 }
12706             }
12707         }else{
12708            if(typeof callback == "function"){
12709                callback(this);
12710            }
12711         }
12712         if(deep === true){
12713             this.expandChildNodes(true);
12714         }
12715     },
12716
12717     isHiddenRoot : function(){
12718         return this.isRoot && !this.getOwnerTree().rootVisible;
12719     },
12720
12721     /**
12722      * Collapse this node.
12723      * @param {Boolean} deep (optional) True to collapse all children as well
12724      * @param {Boolean} anim (optional) false to cancel the default animation
12725      */
12726     collapse : function(deep, anim){
12727         if(this.expanded && !this.isHiddenRoot()){
12728             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
12729                 return;
12730             }
12731             this.expanded = false;
12732             if((this.getOwnerTree().animate && anim !== false) || anim){
12733                 this.ui.animCollapse(function(){
12734                     this.fireEvent("collapse", this);
12735                     if(deep === true){
12736                         this.collapseChildNodes(true);
12737                     }
12738                 }.createDelegate(this));
12739                 return;
12740             }else{
12741                 this.ui.collapse();
12742                 this.fireEvent("collapse", this);
12743             }
12744         }
12745         if(deep === true){
12746             var cs = this.childNodes;
12747             for(var i = 0, len = cs.length; i < len; i++) {
12748                 cs[i].collapse(true, false);
12749             }
12750         }
12751     },
12752
12753     // private
12754     delayedExpand : function(delay){
12755         if(!this.expandProcId){
12756             this.expandProcId = this.expand.defer(delay, this);
12757         }
12758     },
12759
12760     // private
12761     cancelExpand : function(){
12762         if(this.expandProcId){
12763             clearTimeout(this.expandProcId);
12764         }
12765         this.expandProcId = false;
12766     },
12767
12768     /**
12769      * Toggles expanded/collapsed state of the node
12770      */
12771     toggle : function(){
12772         if(this.expanded){
12773             this.collapse();
12774         }else{
12775             this.expand();
12776         }
12777     },
12778
12779     /**
12780      * Ensures all parent nodes are expanded
12781      */
12782     ensureVisible : function(callback){
12783         var tree = this.getOwnerTree();
12784         tree.expandPath(this.parentNode.getPath(), false, function(){
12785             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
12786             Roo.callback(callback);
12787         }.createDelegate(this));
12788     },
12789
12790     /**
12791      * Expand all child nodes
12792      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
12793      */
12794     expandChildNodes : function(deep){
12795         var cs = this.childNodes;
12796         for(var i = 0, len = cs.length; i < len; i++) {
12797                 cs[i].expand(deep);
12798         }
12799     },
12800
12801     /**
12802      * Collapse all child nodes
12803      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
12804      */
12805     collapseChildNodes : function(deep){
12806         var cs = this.childNodes;
12807         for(var i = 0, len = cs.length; i < len; i++) {
12808                 cs[i].collapse(deep);
12809         }
12810     },
12811
12812     /**
12813      * Disables this node
12814      */
12815     disable : function(){
12816         this.disabled = true;
12817         this.unselect();
12818         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12819             this.ui.onDisableChange(this, true);
12820         }
12821         this.fireEvent("disabledchange", this, true);
12822     },
12823
12824     /**
12825      * Enables this node
12826      */
12827     enable : function(){
12828         this.disabled = false;
12829         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12830             this.ui.onDisableChange(this, false);
12831         }
12832         this.fireEvent("disabledchange", this, false);
12833     },
12834
12835     // private
12836     renderChildren : function(suppressEvent){
12837         if(suppressEvent !== false){
12838             this.fireEvent("beforechildrenrendered", this);
12839         }
12840         var cs = this.childNodes;
12841         for(var i = 0, len = cs.length; i < len; i++){
12842             cs[i].render(true);
12843         }
12844         this.childrenRendered = true;
12845     },
12846
12847     // private
12848     sort : function(fn, scope){
12849         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
12850         if(this.childrenRendered){
12851             var cs = this.childNodes;
12852             for(var i = 0, len = cs.length; i < len; i++){
12853                 cs[i].render(true);
12854             }
12855         }
12856     },
12857
12858     // private
12859     render : function(bulkRender){
12860         this.ui.render(bulkRender);
12861         if(!this.rendered){
12862             this.rendered = true;
12863             if(this.expanded){
12864                 this.expanded = false;
12865                 this.expand(false, false);
12866             }
12867         }
12868     },
12869
12870     // private
12871     renderIndent : function(deep, refresh){
12872         if(refresh){
12873             this.ui.childIndent = null;
12874         }
12875         this.ui.renderIndent();
12876         if(deep === true && this.childrenRendered){
12877             var cs = this.childNodes;
12878             for(var i = 0, len = cs.length; i < len; i++){
12879                 cs[i].renderIndent(true, refresh);
12880             }
12881         }
12882     }
12883 });/*
12884  * Based on:
12885  * Ext JS Library 1.1.1
12886  * Copyright(c) 2006-2007, Ext JS, LLC.
12887  *
12888  * Originally Released Under LGPL - original licence link has changed is not relivant.
12889  *
12890  * Fork - LGPL
12891  * <script type="text/javascript">
12892  */
12893  
12894 /**
12895  * @class Roo.tree.AsyncTreeNode
12896  * @extends Roo.tree.TreeNode
12897  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
12898  * @constructor
12899  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
12900  */
12901  Roo.tree.AsyncTreeNode = function(config){
12902     this.loaded = false;
12903     this.loading = false;
12904     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
12905     /**
12906     * @event beforeload
12907     * Fires before this node is loaded, return false to cancel
12908     * @param {Node} this This node
12909     */
12910     this.addEvents({'beforeload':true, 'load': true});
12911     /**
12912     * @event load
12913     * Fires when this node is loaded
12914     * @param {Node} this This node
12915     */
12916     /**
12917      * The loader used by this node (defaults to using the tree's defined loader)
12918      * @type TreeLoader
12919      * @property loader
12920      */
12921 };
12922 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
12923     expand : function(deep, anim, callback){
12924         if(this.loading){ // if an async load is already running, waiting til it's done
12925             var timer;
12926             var f = function(){
12927                 if(!this.loading){ // done loading
12928                     clearInterval(timer);
12929                     this.expand(deep, anim, callback);
12930                 }
12931             }.createDelegate(this);
12932             timer = setInterval(f, 200);
12933             return;
12934         }
12935         if(!this.loaded){
12936             if(this.fireEvent("beforeload", this) === false){
12937                 return;
12938             }
12939             this.loading = true;
12940             this.ui.beforeLoad(this);
12941             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
12942             if(loader){
12943                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
12944                 return;
12945             }
12946         }
12947         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
12948     },
12949     
12950     /**
12951      * Returns true if this node is currently loading
12952      * @return {Boolean}
12953      */
12954     isLoading : function(){
12955         return this.loading;  
12956     },
12957     
12958     loadComplete : function(deep, anim, callback){
12959         this.loading = false;
12960         this.loaded = true;
12961         this.ui.afterLoad(this);
12962         this.fireEvent("load", this);
12963         this.expand(deep, anim, callback);
12964     },
12965     
12966     /**
12967      * Returns true if this node has been loaded
12968      * @return {Boolean}
12969      */
12970     isLoaded : function(){
12971         return this.loaded;
12972     },
12973     
12974     hasChildNodes : function(){
12975         if(!this.isLeaf() && !this.loaded){
12976             return true;
12977         }else{
12978             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
12979         }
12980     },
12981
12982     /**
12983      * Trigger a reload for this node
12984      * @param {Function} callback
12985      */
12986     reload : function(callback){
12987         this.collapse(false, false);
12988         while(this.firstChild){
12989             this.removeChild(this.firstChild);
12990         }
12991         this.childrenRendered = false;
12992         this.loaded = false;
12993         if(this.isHiddenRoot()){
12994             this.expanded = false;
12995         }
12996         this.expand(false, false, callback);
12997     }
12998 });/*
12999  * Based on:
13000  * Ext JS Library 1.1.1
13001  * Copyright(c) 2006-2007, Ext JS, LLC.
13002  *
13003  * Originally Released Under LGPL - original licence link has changed is not relivant.
13004  *
13005  * Fork - LGPL
13006  * <script type="text/javascript">
13007  */
13008  
13009 /**
13010  * @class Roo.tree.TreeNodeUI
13011  * @constructor
13012  * @param {Object} node The node to render
13013  * The TreeNode UI implementation is separate from the
13014  * tree implementation. Unless you are customizing the tree UI,
13015  * you should never have to use this directly.
13016  */
13017 Roo.tree.TreeNodeUI = function(node){
13018     this.node = node;
13019     this.rendered = false;
13020     this.animating = false;
13021     this.emptyIcon = Roo.BLANK_IMAGE_URL;
13022 };
13023
13024 Roo.tree.TreeNodeUI.prototype = {
13025     removeChild : function(node){
13026         if(this.rendered){
13027             this.ctNode.removeChild(node.ui.getEl());
13028         }
13029     },
13030
13031     beforeLoad : function(){
13032          this.addClass("x-tree-node-loading");
13033     },
13034
13035     afterLoad : function(){
13036          this.removeClass("x-tree-node-loading");
13037     },
13038
13039     onTextChange : function(node, text, oldText){
13040         if(this.rendered){
13041             this.textNode.innerHTML = text;
13042         }
13043     },
13044
13045     onDisableChange : function(node, state){
13046         this.disabled = state;
13047         if(state){
13048             this.addClass("x-tree-node-disabled");
13049         }else{
13050             this.removeClass("x-tree-node-disabled");
13051         }
13052     },
13053
13054     onSelectedChange : function(state){
13055         if(state){
13056             this.focus();
13057             this.addClass("x-tree-selected");
13058         }else{
13059             //this.blur();
13060             this.removeClass("x-tree-selected");
13061         }
13062     },
13063
13064     onMove : function(tree, node, oldParent, newParent, index, refNode){
13065         this.childIndent = null;
13066         if(this.rendered){
13067             var targetNode = newParent.ui.getContainer();
13068             if(!targetNode){//target not rendered
13069                 this.holder = document.createElement("div");
13070                 this.holder.appendChild(this.wrap);
13071                 return;
13072             }
13073             var insertBefore = refNode ? refNode.ui.getEl() : null;
13074             if(insertBefore){
13075                 targetNode.insertBefore(this.wrap, insertBefore);
13076             }else{
13077                 targetNode.appendChild(this.wrap);
13078             }
13079             this.node.renderIndent(true);
13080         }
13081     },
13082
13083     addClass : function(cls){
13084         if(this.elNode){
13085             Roo.fly(this.elNode).addClass(cls);
13086         }
13087     },
13088
13089     removeClass : function(cls){
13090         if(this.elNode){
13091             Roo.fly(this.elNode).removeClass(cls);
13092         }
13093     },
13094
13095     remove : function(){
13096         if(this.rendered){
13097             this.holder = document.createElement("div");
13098             this.holder.appendChild(this.wrap);
13099         }
13100     },
13101
13102     fireEvent : function(){
13103         return this.node.fireEvent.apply(this.node, arguments);
13104     },
13105
13106     initEvents : function(){
13107         this.node.on("move", this.onMove, this);
13108         var E = Roo.EventManager;
13109         var a = this.anchor;
13110
13111         var el = Roo.fly(a, '_treeui');
13112
13113         if(Roo.isOpera){ // opera render bug ignores the CSS
13114             el.setStyle("text-decoration", "none");
13115         }
13116
13117         el.on("click", this.onClick, this);
13118         el.on("dblclick", this.onDblClick, this);
13119
13120         if(this.checkbox){
13121             Roo.EventManager.on(this.checkbox,
13122                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
13123         }
13124
13125         el.on("contextmenu", this.onContextMenu, this);
13126
13127         var icon = Roo.fly(this.iconNode);
13128         icon.on("click", this.onClick, this);
13129         icon.on("dblclick", this.onDblClick, this);
13130         icon.on("contextmenu", this.onContextMenu, this);
13131         E.on(this.ecNode, "click", this.ecClick, this, true);
13132
13133         if(this.node.disabled){
13134             this.addClass("x-tree-node-disabled");
13135         }
13136         if(this.node.hidden){
13137             this.addClass("x-tree-node-disabled");
13138         }
13139         var ot = this.node.getOwnerTree();
13140         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
13141         if(dd && (!this.node.isRoot || ot.rootVisible)){
13142             Roo.dd.Registry.register(this.elNode, {
13143                 node: this.node,
13144                 handles: this.getDDHandles(),
13145                 isHandle: false
13146             });
13147         }
13148     },
13149
13150     getDDHandles : function(){
13151         return [this.iconNode, this.textNode];
13152     },
13153
13154     hide : function(){
13155         if(this.rendered){
13156             this.wrap.style.display = "none";
13157         }
13158     },
13159
13160     show : function(){
13161         if(this.rendered){
13162             this.wrap.style.display = "";
13163         }
13164     },
13165
13166     onContextMenu : function(e){
13167         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
13168             e.preventDefault();
13169             this.focus();
13170             this.fireEvent("contextmenu", this.node, e);
13171         }
13172     },
13173
13174     onClick : function(e){
13175         if(this.dropping){
13176             e.stopEvent();
13177             return;
13178         }
13179         if(this.fireEvent("beforeclick", this.node, e) !== false){
13180             if(!this.disabled && this.node.attributes.href){
13181                 this.fireEvent("click", this.node, e);
13182                 return;
13183             }
13184             e.preventDefault();
13185             if(this.disabled){
13186                 return;
13187             }
13188
13189             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
13190                 this.node.toggle();
13191             }
13192
13193             this.fireEvent("click", this.node, e);
13194         }else{
13195             e.stopEvent();
13196         }
13197     },
13198
13199     onDblClick : function(e){
13200         e.preventDefault();
13201         if(this.disabled){
13202             return;
13203         }
13204         if(this.checkbox){
13205             this.toggleCheck();
13206         }
13207         if(!this.animating && this.node.hasChildNodes()){
13208             this.node.toggle();
13209         }
13210         this.fireEvent("dblclick", this.node, e);
13211     },
13212
13213     onCheckChange : function(){
13214         var checked = this.checkbox.checked;
13215         this.node.attributes.checked = checked;
13216         this.fireEvent('checkchange', this.node, checked);
13217     },
13218
13219     ecClick : function(e){
13220         if(!this.animating && this.node.hasChildNodes()){
13221             this.node.toggle();
13222         }
13223     },
13224
13225     startDrop : function(){
13226         this.dropping = true;
13227     },
13228
13229     // delayed drop so the click event doesn't get fired on a drop
13230     endDrop : function(){
13231        setTimeout(function(){
13232            this.dropping = false;
13233        }.createDelegate(this), 50);
13234     },
13235
13236     expand : function(){
13237         this.updateExpandIcon();
13238         this.ctNode.style.display = "";
13239     },
13240
13241     focus : function(){
13242         if(!this.node.preventHScroll){
13243             try{this.anchor.focus();
13244             }catch(e){}
13245         }else if(!Roo.isIE){
13246             try{
13247                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
13248                 var l = noscroll.scrollLeft;
13249                 this.anchor.focus();
13250                 noscroll.scrollLeft = l;
13251             }catch(e){}
13252         }
13253     },
13254
13255     toggleCheck : function(value){
13256         var cb = this.checkbox;
13257         if(cb){
13258             cb.checked = (value === undefined ? !cb.checked : value);
13259         }
13260     },
13261
13262     blur : function(){
13263         try{
13264             this.anchor.blur();
13265         }catch(e){}
13266     },
13267
13268     animExpand : function(callback){
13269         var ct = Roo.get(this.ctNode);
13270         ct.stopFx();
13271         if(!this.node.hasChildNodes()){
13272             this.updateExpandIcon();
13273             this.ctNode.style.display = "";
13274             Roo.callback(callback);
13275             return;
13276         }
13277         this.animating = true;
13278         this.updateExpandIcon();
13279
13280         ct.slideIn('t', {
13281            callback : function(){
13282                this.animating = false;
13283                Roo.callback(callback);
13284             },
13285             scope: this,
13286             duration: this.node.ownerTree.duration || .25
13287         });
13288     },
13289
13290     highlight : function(){
13291         var tree = this.node.getOwnerTree();
13292         Roo.fly(this.wrap).highlight(
13293             tree.hlColor || "C3DAF9",
13294             {endColor: tree.hlBaseColor}
13295         );
13296     },
13297
13298     collapse : function(){
13299         this.updateExpandIcon();
13300         this.ctNode.style.display = "none";
13301     },
13302
13303     animCollapse : function(callback){
13304         var ct = Roo.get(this.ctNode);
13305         ct.enableDisplayMode('block');
13306         ct.stopFx();
13307
13308         this.animating = true;
13309         this.updateExpandIcon();
13310
13311         ct.slideOut('t', {
13312             callback : function(){
13313                this.animating = false;
13314                Roo.callback(callback);
13315             },
13316             scope: this,
13317             duration: this.node.ownerTree.duration || .25
13318         });
13319     },
13320
13321     getContainer : function(){
13322         return this.ctNode;
13323     },
13324
13325     getEl : function(){
13326         return this.wrap;
13327     },
13328
13329     appendDDGhost : function(ghostNode){
13330         ghostNode.appendChild(this.elNode.cloneNode(true));
13331     },
13332
13333     getDDRepairXY : function(){
13334         return Roo.lib.Dom.getXY(this.iconNode);
13335     },
13336
13337     onRender : function(){
13338         this.render();
13339     },
13340
13341     render : function(bulkRender){
13342         var n = this.node, a = n.attributes;
13343         var targetNode = n.parentNode ?
13344               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
13345
13346         if(!this.rendered){
13347             this.rendered = true;
13348
13349             this.renderElements(n, a, targetNode, bulkRender);
13350
13351             if(a.qtip){
13352                if(this.textNode.setAttributeNS){
13353                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
13354                    if(a.qtipTitle){
13355                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
13356                    }
13357                }else{
13358                    this.textNode.setAttribute("ext:qtip", a.qtip);
13359                    if(a.qtipTitle){
13360                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
13361                    }
13362                }
13363             }else if(a.qtipCfg){
13364                 a.qtipCfg.target = Roo.id(this.textNode);
13365                 Roo.QuickTips.register(a.qtipCfg);
13366             }
13367             this.initEvents();
13368             if(!this.node.expanded){
13369                 this.updateExpandIcon();
13370             }
13371         }else{
13372             if(bulkRender === true) {
13373                 targetNode.appendChild(this.wrap);
13374             }
13375         }
13376     },
13377
13378     renderElements : function(n, a, targetNode, bulkRender)
13379     {
13380         // add some indent caching, this helps performance when rendering a large tree
13381         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
13382         var t = n.getOwnerTree();
13383         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
13384         if (typeof(n.attributes.html) != 'undefined') {
13385             txt = n.attributes.html;
13386         }
13387         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
13388         var cb = typeof a.checked == 'boolean';
13389         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
13390         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
13391             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
13392             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
13393             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
13394             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
13395             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
13396              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
13397                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
13398             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
13399             "</li>"];
13400
13401         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
13402             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
13403                                 n.nextSibling.ui.getEl(), buf.join(""));
13404         }else{
13405             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
13406         }
13407
13408         this.elNode = this.wrap.childNodes[0];
13409         this.ctNode = this.wrap.childNodes[1];
13410         var cs = this.elNode.childNodes;
13411         this.indentNode = cs[0];
13412         this.ecNode = cs[1];
13413         this.iconNode = cs[2];
13414         var index = 3;
13415         if(cb){
13416             this.checkbox = cs[3];
13417             index++;
13418         }
13419         this.anchor = cs[index];
13420         this.textNode = cs[index].firstChild;
13421     },
13422
13423     getAnchor : function(){
13424         return this.anchor;
13425     },
13426
13427     getTextEl : function(){
13428         return this.textNode;
13429     },
13430
13431     getIconEl : function(){
13432         return this.iconNode;
13433     },
13434
13435     isChecked : function(){
13436         return this.checkbox ? this.checkbox.checked : false;
13437     },
13438
13439     updateExpandIcon : function(){
13440         if(this.rendered){
13441             var n = this.node, c1, c2;
13442             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
13443             var hasChild = n.hasChildNodes();
13444             if(hasChild){
13445                 if(n.expanded){
13446                     cls += "-minus";
13447                     c1 = "x-tree-node-collapsed";
13448                     c2 = "x-tree-node-expanded";
13449                 }else{
13450                     cls += "-plus";
13451                     c1 = "x-tree-node-expanded";
13452                     c2 = "x-tree-node-collapsed";
13453                 }
13454                 if(this.wasLeaf){
13455                     this.removeClass("x-tree-node-leaf");
13456                     this.wasLeaf = false;
13457                 }
13458                 if(this.c1 != c1 || this.c2 != c2){
13459                     Roo.fly(this.elNode).replaceClass(c1, c2);
13460                     this.c1 = c1; this.c2 = c2;
13461                 }
13462             }else{
13463                 // this changes non-leafs into leafs if they have no children.
13464                 // it's not very rational behaviour..
13465                 
13466                 if(!this.wasLeaf && this.node.leaf){
13467                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
13468                     delete this.c1;
13469                     delete this.c2;
13470                     this.wasLeaf = true;
13471                 }
13472             }
13473             var ecc = "x-tree-ec-icon "+cls;
13474             if(this.ecc != ecc){
13475                 this.ecNode.className = ecc;
13476                 this.ecc = ecc;
13477             }
13478         }
13479     },
13480
13481     getChildIndent : function(){
13482         if(!this.childIndent){
13483             var buf = [];
13484             var p = this.node;
13485             while(p){
13486                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
13487                     if(!p.isLast()) {
13488                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
13489                     } else {
13490                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
13491                     }
13492                 }
13493                 p = p.parentNode;
13494             }
13495             this.childIndent = buf.join("");
13496         }
13497         return this.childIndent;
13498     },
13499
13500     renderIndent : function(){
13501         if(this.rendered){
13502             var indent = "";
13503             var p = this.node.parentNode;
13504             if(p){
13505                 indent = p.ui.getChildIndent();
13506             }
13507             if(this.indentMarkup != indent){ // don't rerender if not required
13508                 this.indentNode.innerHTML = indent;
13509                 this.indentMarkup = indent;
13510             }
13511             this.updateExpandIcon();
13512         }
13513     }
13514 };
13515
13516 Roo.tree.RootTreeNodeUI = function(){
13517     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
13518 };
13519 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
13520     render : function(){
13521         if(!this.rendered){
13522             var targetNode = this.node.ownerTree.innerCt.dom;
13523             this.node.expanded = true;
13524             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
13525             this.wrap = this.ctNode = targetNode.firstChild;
13526         }
13527     },
13528     collapse : function(){
13529     },
13530     expand : function(){
13531     }
13532 });/*
13533  * Based on:
13534  * Ext JS Library 1.1.1
13535  * Copyright(c) 2006-2007, Ext JS, LLC.
13536  *
13537  * Originally Released Under LGPL - original licence link has changed is not relivant.
13538  *
13539  * Fork - LGPL
13540  * <script type="text/javascript">
13541  */
13542 /**
13543  * @class Roo.tree.TreeLoader
13544  * @extends Roo.util.Observable
13545  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
13546  * nodes from a specified URL. The response must be a javascript Array definition
13547  * who's elements are node definition objects. eg:
13548  * <pre><code>
13549 {  success : true,
13550    data :      [
13551    
13552     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
13553     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
13554     ]
13555 }
13556
13557
13558 </code></pre>
13559  * <br><br>
13560  * The old style respose with just an array is still supported, but not recommended.
13561  * <br><br>
13562  *
13563  * A server request is sent, and child nodes are loaded only when a node is expanded.
13564  * The loading node's id is passed to the server under the parameter name "node" to
13565  * enable the server to produce the correct child nodes.
13566  * <br><br>
13567  * To pass extra parameters, an event handler may be attached to the "beforeload"
13568  * event, and the parameters specified in the TreeLoader's baseParams property:
13569  * <pre><code>
13570     myTreeLoader.on("beforeload", function(treeLoader, node) {
13571         this.baseParams.category = node.attributes.category;
13572     }, this);
13573     
13574 </code></pre>
13575  *
13576  * This would pass an HTTP parameter called "category" to the server containing
13577  * the value of the Node's "category" attribute.
13578  * @constructor
13579  * Creates a new Treeloader.
13580  * @param {Object} config A config object containing config properties.
13581  */
13582 Roo.tree.TreeLoader = function(config){
13583     this.baseParams = {};
13584     this.requestMethod = "POST";
13585     Roo.apply(this, config);
13586
13587     this.addEvents({
13588     
13589         /**
13590          * @event beforeload
13591          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
13592          * @param {Object} This TreeLoader object.
13593          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13594          * @param {Object} callback The callback function specified in the {@link #load} call.
13595          */
13596         beforeload : true,
13597         /**
13598          * @event load
13599          * Fires when the node has been successfuly loaded.
13600          * @param {Object} This TreeLoader object.
13601          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13602          * @param {Object} response The response object containing the data from the server.
13603          */
13604         load : true,
13605         /**
13606          * @event loadexception
13607          * Fires if the network request failed.
13608          * @param {Object} This TreeLoader object.
13609          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13610          * @param {Object} response The response object containing the data from the server.
13611          */
13612         loadexception : true,
13613         /**
13614          * @event create
13615          * Fires before a node is created, enabling you to return custom Node types 
13616          * @param {Object} This TreeLoader object.
13617          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
13618          */
13619         create : true
13620     });
13621
13622     Roo.tree.TreeLoader.superclass.constructor.call(this);
13623 };
13624
13625 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
13626     /**
13627     * @cfg {String} dataUrl The URL from which to request a Json string which
13628     * specifies an array of node definition object representing the child nodes
13629     * to be loaded.
13630     */
13631     /**
13632     * @cfg {String} requestMethod either GET or POST
13633     * defaults to POST (due to BC)
13634     * to be loaded.
13635     */
13636     /**
13637     * @cfg {Object} baseParams (optional) An object containing properties which
13638     * specify HTTP parameters to be passed to each request for child nodes.
13639     */
13640     /**
13641     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
13642     * created by this loader. If the attributes sent by the server have an attribute in this object,
13643     * they take priority.
13644     */
13645     /**
13646     * @cfg {Object} uiProviders (optional) An object containing properties which
13647     * 
13648     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
13649     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
13650     * <i>uiProvider</i> attribute of a returned child node is a string rather
13651     * than a reference to a TreeNodeUI implementation, this that string value
13652     * is used as a property name in the uiProviders object. You can define the provider named
13653     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
13654     */
13655     uiProviders : {},
13656
13657     /**
13658     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
13659     * child nodes before loading.
13660     */
13661     clearOnLoad : true,
13662
13663     /**
13664     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
13665     * property on loading, rather than expecting an array. (eg. more compatible to a standard
13666     * Grid query { data : [ .....] }
13667     */
13668     
13669     root : false,
13670      /**
13671     * @cfg {String} queryParam (optional) 
13672     * Name of the query as it will be passed on the querystring (defaults to 'node')
13673     * eg. the request will be ?node=[id]
13674     */
13675     
13676     
13677     queryParam: false,
13678     
13679     /**
13680      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
13681      * This is called automatically when a node is expanded, but may be used to reload
13682      * a node (or append new children if the {@link #clearOnLoad} option is false.)
13683      * @param {Roo.tree.TreeNode} node
13684      * @param {Function} callback
13685      */
13686     load : function(node, callback){
13687         if(this.clearOnLoad){
13688             while(node.firstChild){
13689                 node.removeChild(node.firstChild);
13690             }
13691         }
13692         if(node.attributes.children){ // preloaded json children
13693             var cs = node.attributes.children;
13694             for(var i = 0, len = cs.length; i < len; i++){
13695                 Roo.log('appendchild');
13696                 Roo.log(cs[i]);
13697                 node.appendChild(this.createNode(cs[i]));
13698             }
13699             if(typeof callback == "function"){
13700                 callback();
13701             }
13702         }else if(this.dataUrl){
13703             this.requestData(node, callback);
13704         }
13705     },
13706
13707     getParams: function(node){
13708         var buf = [], bp = this.baseParams;
13709         for(var key in bp){
13710             if(typeof bp[key] != "function"){
13711                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
13712             }
13713         }
13714         var n = this.queryParam === false ? 'node' : this.queryParam;
13715         buf.push(n + "=", encodeURIComponent(node.id));
13716         return buf.join("");
13717     },
13718
13719     requestData : function(node, callback){
13720         if(this.fireEvent("beforeload", this, node, callback) !== false){
13721             this.transId = Roo.Ajax.request({
13722                 method:this.requestMethod,
13723                 url: this.dataUrl||this.url,
13724                 success: this.handleResponse,
13725                 failure: this.handleFailure,
13726                 scope: this,
13727                 argument: {callback: callback, node: node},
13728                 params: this.getParams(node)
13729             });
13730         }else{
13731             // if the load is cancelled, make sure we notify
13732             // the node that we are done
13733             if(typeof callback == "function"){
13734                 callback();
13735             }
13736         }
13737     },
13738
13739     isLoading : function(){
13740         return this.transId ? true : false;
13741     },
13742
13743     abort : function(){
13744         if(this.isLoading()){
13745             Roo.Ajax.abort(this.transId);
13746         }
13747     },
13748
13749     // private
13750     createNode : function(attr)
13751     {
13752         // apply baseAttrs, nice idea Corey!
13753         if(this.baseAttrs){
13754             Roo.applyIf(attr, this.baseAttrs);
13755         }
13756         if(this.applyLoader !== false){
13757             attr.loader = this;
13758         }
13759         // uiProvider = depreciated..
13760         
13761         if(typeof(attr.uiProvider) == 'string'){
13762            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
13763                 /**  eval:var:attr */ eval(attr.uiProvider);
13764         }
13765         if(typeof(this.uiProviders['default']) != 'undefined') {
13766             attr.uiProvider = this.uiProviders['default'];
13767         }
13768         
13769         this.fireEvent('create', this, attr);
13770         
13771         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
13772         return(attr.leaf ?
13773                         new Roo.tree.TreeNode(attr) :
13774                         new Roo.tree.AsyncTreeNode(attr));
13775     },
13776
13777     processResponse : function(response, node, callback)
13778     {
13779         var json = response.responseText;
13780         try {
13781             
13782             var o = Roo.decode(json);
13783             
13784             if (this.root === false && typeof(o.success) != undefined) {
13785                 this.root = 'data'; // the default behaviour for list like data..
13786                 }
13787                 
13788             if (this.root !== false &&  !o.success) {
13789                 // it's a failure condition.
13790                 var a = response.argument;
13791                 this.fireEvent("loadexception", this, a.node, response);
13792                 Roo.log("Load failed - should have a handler really");
13793                 return;
13794             }
13795             
13796             
13797             
13798             if (this.root !== false) {
13799                  o = o[this.root];
13800             }
13801             
13802             for(var i = 0, len = o.length; i < len; i++){
13803                 var n = this.createNode(o[i]);
13804                 if(n){
13805                     node.appendChild(n);
13806                 }
13807             }
13808             if(typeof callback == "function"){
13809                 callback(this, node);
13810             }
13811         }catch(e){
13812             this.handleFailure(response);
13813         }
13814     },
13815
13816     handleResponse : function(response){
13817         this.transId = false;
13818         var a = response.argument;
13819         this.processResponse(response, a.node, a.callback);
13820         this.fireEvent("load", this, a.node, response);
13821     },
13822
13823     handleFailure : function(response)
13824     {
13825         // should handle failure better..
13826         this.transId = false;
13827         var a = response.argument;
13828         this.fireEvent("loadexception", this, a.node, response);
13829         if(typeof a.callback == "function"){
13830             a.callback(this, a.node);
13831         }
13832     }
13833 });/*
13834  * Based on:
13835  * Ext JS Library 1.1.1
13836  * Copyright(c) 2006-2007, Ext JS, LLC.
13837  *
13838  * Originally Released Under LGPL - original licence link has changed is not relivant.
13839  *
13840  * Fork - LGPL
13841  * <script type="text/javascript">
13842  */
13843
13844 /**
13845 * @class Roo.tree.TreeFilter
13846 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
13847 * @param {TreePanel} tree
13848 * @param {Object} config (optional)
13849  */
13850 Roo.tree.TreeFilter = function(tree, config){
13851     this.tree = tree;
13852     this.filtered = {};
13853     Roo.apply(this, config);
13854 };
13855
13856 Roo.tree.TreeFilter.prototype = {
13857     clearBlank:false,
13858     reverse:false,
13859     autoClear:false,
13860     remove:false,
13861
13862      /**
13863      * Filter the data by a specific attribute.
13864      * @param {String/RegExp} value Either string that the attribute value
13865      * should start with or a RegExp to test against the attribute
13866      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
13867      * @param {TreeNode} startNode (optional) The node to start the filter at.
13868      */
13869     filter : function(value, attr, startNode){
13870         attr = attr || "text";
13871         var f;
13872         if(typeof value == "string"){
13873             var vlen = value.length;
13874             // auto clear empty filter
13875             if(vlen == 0 && this.clearBlank){
13876                 this.clear();
13877                 return;
13878             }
13879             value = value.toLowerCase();
13880             f = function(n){
13881                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
13882             };
13883         }else if(value.exec){ // regex?
13884             f = function(n){
13885                 return value.test(n.attributes[attr]);
13886             };
13887         }else{
13888             throw 'Illegal filter type, must be string or regex';
13889         }
13890         this.filterBy(f, null, startNode);
13891         },
13892
13893     /**
13894      * Filter by a function. The passed function will be called with each
13895      * node in the tree (or from the startNode). If the function returns true, the node is kept
13896      * otherwise it is filtered. If a node is filtered, its children are also filtered.
13897      * @param {Function} fn The filter function
13898      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
13899      */
13900     filterBy : function(fn, scope, startNode){
13901         startNode = startNode || this.tree.root;
13902         if(this.autoClear){
13903             this.clear();
13904         }
13905         var af = this.filtered, rv = this.reverse;
13906         var f = function(n){
13907             if(n == startNode){
13908                 return true;
13909             }
13910             if(af[n.id]){
13911                 return false;
13912             }
13913             var m = fn.call(scope || n, n);
13914             if(!m || rv){
13915                 af[n.id] = n;
13916                 n.ui.hide();
13917                 return false;
13918             }
13919             return true;
13920         };
13921         startNode.cascade(f);
13922         if(this.remove){
13923            for(var id in af){
13924                if(typeof id != "function"){
13925                    var n = af[id];
13926                    if(n && n.parentNode){
13927                        n.parentNode.removeChild(n);
13928                    }
13929                }
13930            }
13931         }
13932     },
13933
13934     /**
13935      * Clears the current filter. Note: with the "remove" option
13936      * set a filter cannot be cleared.
13937      */
13938     clear : function(){
13939         var t = this.tree;
13940         var af = this.filtered;
13941         for(var id in af){
13942             if(typeof id != "function"){
13943                 var n = af[id];
13944                 if(n){
13945                     n.ui.show();
13946                 }
13947             }
13948         }
13949         this.filtered = {};
13950     }
13951 };
13952 /*
13953  * Based on:
13954  * Ext JS Library 1.1.1
13955  * Copyright(c) 2006-2007, Ext JS, LLC.
13956  *
13957  * Originally Released Under LGPL - original licence link has changed is not relivant.
13958  *
13959  * Fork - LGPL
13960  * <script type="text/javascript">
13961  */
13962  
13963
13964 /**
13965  * @class Roo.tree.TreeSorter
13966  * Provides sorting of nodes in a TreePanel
13967  * 
13968  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
13969  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
13970  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
13971  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
13972  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
13973  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
13974  * @constructor
13975  * @param {TreePanel} tree
13976  * @param {Object} config
13977  */
13978 Roo.tree.TreeSorter = function(tree, config){
13979     Roo.apply(this, config);
13980     tree.on("beforechildrenrendered", this.doSort, this);
13981     tree.on("append", this.updateSort, this);
13982     tree.on("insert", this.updateSort, this);
13983     
13984     var dsc = this.dir && this.dir.toLowerCase() == "desc";
13985     var p = this.property || "text";
13986     var sortType = this.sortType;
13987     var fs = this.folderSort;
13988     var cs = this.caseSensitive === true;
13989     var leafAttr = this.leafAttr || 'leaf';
13990
13991     this.sortFn = function(n1, n2){
13992         if(fs){
13993             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
13994                 return 1;
13995             }
13996             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
13997                 return -1;
13998             }
13999         }
14000         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
14001         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
14002         if(v1 < v2){
14003                         return dsc ? +1 : -1;
14004                 }else if(v1 > v2){
14005                         return dsc ? -1 : +1;
14006         }else{
14007                 return 0;
14008         }
14009     };
14010 };
14011
14012 Roo.tree.TreeSorter.prototype = {
14013     doSort : function(node){
14014         node.sort(this.sortFn);
14015     },
14016     
14017     compareNodes : function(n1, n2){
14018         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
14019     },
14020     
14021     updateSort : function(tree, node){
14022         if(node.childrenRendered){
14023             this.doSort.defer(1, this, [node]);
14024         }
14025     }
14026 };/*
14027  * Based on:
14028  * Ext JS Library 1.1.1
14029  * Copyright(c) 2006-2007, Ext JS, LLC.
14030  *
14031  * Originally Released Under LGPL - original licence link has changed is not relivant.
14032  *
14033  * Fork - LGPL
14034  * <script type="text/javascript">
14035  */
14036
14037 if(Roo.dd.DropZone){
14038     
14039 Roo.tree.TreeDropZone = function(tree, config){
14040     this.allowParentInsert = false;
14041     this.allowContainerDrop = false;
14042     this.appendOnly = false;
14043     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
14044     this.tree = tree;
14045     this.lastInsertClass = "x-tree-no-status";
14046     this.dragOverData = {};
14047 };
14048
14049 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
14050     ddGroup : "TreeDD",
14051     scroll:  true,
14052     
14053     expandDelay : 1000,
14054     
14055     expandNode : function(node){
14056         if(node.hasChildNodes() && !node.isExpanded()){
14057             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
14058         }
14059     },
14060     
14061     queueExpand : function(node){
14062         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
14063     },
14064     
14065     cancelExpand : function(){
14066         if(this.expandProcId){
14067             clearTimeout(this.expandProcId);
14068             this.expandProcId = false;
14069         }
14070     },
14071     
14072     isValidDropPoint : function(n, pt, dd, e, data){
14073         if(!n || !data){ return false; }
14074         var targetNode = n.node;
14075         var dropNode = data.node;
14076         // default drop rules
14077         if(!(targetNode && targetNode.isTarget && pt)){
14078             return false;
14079         }
14080         if(pt == "append" && targetNode.allowChildren === false){
14081             return false;
14082         }
14083         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
14084             return false;
14085         }
14086         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
14087             return false;
14088         }
14089         // reuse the object
14090         var overEvent = this.dragOverData;
14091         overEvent.tree = this.tree;
14092         overEvent.target = targetNode;
14093         overEvent.data = data;
14094         overEvent.point = pt;
14095         overEvent.source = dd;
14096         overEvent.rawEvent = e;
14097         overEvent.dropNode = dropNode;
14098         overEvent.cancel = false;  
14099         var result = this.tree.fireEvent("nodedragover", overEvent);
14100         return overEvent.cancel === false && result !== false;
14101     },
14102     
14103     getDropPoint : function(e, n, dd)
14104     {
14105         var tn = n.node;
14106         if(tn.isRoot){
14107             return tn.allowChildren !== false ? "append" : false; // always append for root
14108         }
14109         var dragEl = n.ddel;
14110         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
14111         var y = Roo.lib.Event.getPageY(e);
14112         //var noAppend = tn.allowChildren === false || tn.isLeaf();
14113         
14114         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
14115         var noAppend = tn.allowChildren === false;
14116         if(this.appendOnly || tn.parentNode.allowChildren === false){
14117             return noAppend ? false : "append";
14118         }
14119         var noBelow = false;
14120         if(!this.allowParentInsert){
14121             noBelow = tn.hasChildNodes() && tn.isExpanded();
14122         }
14123         var q = (b - t) / (noAppend ? 2 : 3);
14124         if(y >= t && y < (t + q)){
14125             return "above";
14126         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
14127             return "below";
14128         }else{
14129             return "append";
14130         }
14131     },
14132     
14133     onNodeEnter : function(n, dd, e, data)
14134     {
14135         this.cancelExpand();
14136     },
14137     
14138     onNodeOver : function(n, dd, e, data)
14139     {
14140        
14141         var pt = this.getDropPoint(e, n, dd);
14142         var node = n.node;
14143         
14144         // auto node expand check
14145         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
14146             this.queueExpand(node);
14147         }else if(pt != "append"){
14148             this.cancelExpand();
14149         }
14150         
14151         // set the insert point style on the target node
14152         var returnCls = this.dropNotAllowed;
14153         if(this.isValidDropPoint(n, pt, dd, e, data)){
14154            if(pt){
14155                var el = n.ddel;
14156                var cls;
14157                if(pt == "above"){
14158                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
14159                    cls = "x-tree-drag-insert-above";
14160                }else if(pt == "below"){
14161                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
14162                    cls = "x-tree-drag-insert-below";
14163                }else{
14164                    returnCls = "x-tree-drop-ok-append";
14165                    cls = "x-tree-drag-append";
14166                }
14167                if(this.lastInsertClass != cls){
14168                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
14169                    this.lastInsertClass = cls;
14170                }
14171            }
14172        }
14173        return returnCls;
14174     },
14175     
14176     onNodeOut : function(n, dd, e, data){
14177         
14178         this.cancelExpand();
14179         this.removeDropIndicators(n);
14180     },
14181     
14182     onNodeDrop : function(n, dd, e, data){
14183         var point = this.getDropPoint(e, n, dd);
14184         var targetNode = n.node;
14185         targetNode.ui.startDrop();
14186         if(!this.isValidDropPoint(n, point, dd, e, data)){
14187             targetNode.ui.endDrop();
14188             return false;
14189         }
14190         // first try to find the drop node
14191         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
14192         var dropEvent = {
14193             tree : this.tree,
14194             target: targetNode,
14195             data: data,
14196             point: point,
14197             source: dd,
14198             rawEvent: e,
14199             dropNode: dropNode,
14200             cancel: !dropNode   
14201         };
14202         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
14203         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
14204             targetNode.ui.endDrop();
14205             return false;
14206         }
14207         // allow target changing
14208         targetNode = dropEvent.target;
14209         if(point == "append" && !targetNode.isExpanded()){
14210             targetNode.expand(false, null, function(){
14211                 this.completeDrop(dropEvent);
14212             }.createDelegate(this));
14213         }else{
14214             this.completeDrop(dropEvent);
14215         }
14216         return true;
14217     },
14218     
14219     completeDrop : function(de){
14220         var ns = de.dropNode, p = de.point, t = de.target;
14221         if(!(ns instanceof Array)){
14222             ns = [ns];
14223         }
14224         var n;
14225         for(var i = 0, len = ns.length; i < len; i++){
14226             n = ns[i];
14227             if(p == "above"){
14228                 t.parentNode.insertBefore(n, t);
14229             }else if(p == "below"){
14230                 t.parentNode.insertBefore(n, t.nextSibling);
14231             }else{
14232                 t.appendChild(n);
14233             }
14234         }
14235         n.ui.focus();
14236         if(this.tree.hlDrop){
14237             n.ui.highlight();
14238         }
14239         t.ui.endDrop();
14240         this.tree.fireEvent("nodedrop", de);
14241     },
14242     
14243     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
14244         if(this.tree.hlDrop){
14245             dropNode.ui.focus();
14246             dropNode.ui.highlight();
14247         }
14248         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
14249     },
14250     
14251     getTree : function(){
14252         return this.tree;
14253     },
14254     
14255     removeDropIndicators : function(n){
14256         if(n && n.ddel){
14257             var el = n.ddel;
14258             Roo.fly(el).removeClass([
14259                     "x-tree-drag-insert-above",
14260                     "x-tree-drag-insert-below",
14261                     "x-tree-drag-append"]);
14262             this.lastInsertClass = "_noclass";
14263         }
14264     },
14265     
14266     beforeDragDrop : function(target, e, id){
14267         this.cancelExpand();
14268         return true;
14269     },
14270     
14271     afterRepair : function(data){
14272         if(data && Roo.enableFx){
14273             data.node.ui.highlight();
14274         }
14275         this.hideProxy();
14276     } 
14277     
14278 });
14279
14280 }
14281 /*
14282  * Based on:
14283  * Ext JS Library 1.1.1
14284  * Copyright(c) 2006-2007, Ext JS, LLC.
14285  *
14286  * Originally Released Under LGPL - original licence link has changed is not relivant.
14287  *
14288  * Fork - LGPL
14289  * <script type="text/javascript">
14290  */
14291  
14292
14293 if(Roo.dd.DragZone){
14294 Roo.tree.TreeDragZone = function(tree, config){
14295     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
14296     this.tree = tree;
14297 };
14298
14299 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
14300     ddGroup : "TreeDD",
14301    
14302     onBeforeDrag : function(data, e){
14303         var n = data.node;
14304         return n && n.draggable && !n.disabled;
14305     },
14306      
14307     
14308     onInitDrag : function(e){
14309         var data = this.dragData;
14310         this.tree.getSelectionModel().select(data.node);
14311         this.proxy.update("");
14312         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
14313         this.tree.fireEvent("startdrag", this.tree, data.node, e);
14314     },
14315     
14316     getRepairXY : function(e, data){
14317         return data.node.ui.getDDRepairXY();
14318     },
14319     
14320     onEndDrag : function(data, e){
14321         this.tree.fireEvent("enddrag", this.tree, data.node, e);
14322         
14323         
14324     },
14325     
14326     onValidDrop : function(dd, e, id){
14327         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
14328         this.hideProxy();
14329     },
14330     
14331     beforeInvalidDrop : function(e, id){
14332         // this scrolls the original position back into view
14333         var sm = this.tree.getSelectionModel();
14334         sm.clearSelections();
14335         sm.select(this.dragData.node);
14336     }
14337 });
14338 }/*
14339  * Based on:
14340  * Ext JS Library 1.1.1
14341  * Copyright(c) 2006-2007, Ext JS, LLC.
14342  *
14343  * Originally Released Under LGPL - original licence link has changed is not relivant.
14344  *
14345  * Fork - LGPL
14346  * <script type="text/javascript">
14347  */
14348 /**
14349  * @class Roo.tree.TreeEditor
14350  * @extends Roo.Editor
14351  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
14352  * as the editor field.
14353  * @constructor
14354  * @param {Object} config (used to be the tree panel.)
14355  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
14356  * 
14357  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
14358  * @cfg {Roo.form.TextField|Object} field The field configuration
14359  *
14360  * 
14361  */
14362 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
14363     var tree = config;
14364     var field;
14365     if (oldconfig) { // old style..
14366         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
14367     } else {
14368         // new style..
14369         tree = config.tree;
14370         config.field = config.field  || {};
14371         config.field.xtype = 'TextField';
14372         field = Roo.factory(config.field, Roo.form);
14373     }
14374     config = config || {};
14375     
14376     
14377     this.addEvents({
14378         /**
14379          * @event beforenodeedit
14380          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
14381          * false from the handler of this event.
14382          * @param {Editor} this
14383          * @param {Roo.tree.Node} node 
14384          */
14385         "beforenodeedit" : true
14386     });
14387     
14388     //Roo.log(config);
14389     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
14390
14391     this.tree = tree;
14392
14393     tree.on('beforeclick', this.beforeNodeClick, this);
14394     tree.getTreeEl().on('mousedown', this.hide, this);
14395     this.on('complete', this.updateNode, this);
14396     this.on('beforestartedit', this.fitToTree, this);
14397     this.on('startedit', this.bindScroll, this, {delay:10});
14398     this.on('specialkey', this.onSpecialKey, this);
14399 };
14400
14401 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
14402     /**
14403      * @cfg {String} alignment
14404      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
14405      */
14406     alignment: "l-l",
14407     // inherit
14408     autoSize: false,
14409     /**
14410      * @cfg {Boolean} hideEl
14411      * True to hide the bound element while the editor is displayed (defaults to false)
14412      */
14413     hideEl : false,
14414     /**
14415      * @cfg {String} cls
14416      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
14417      */
14418     cls: "x-small-editor x-tree-editor",
14419     /**
14420      * @cfg {Boolean} shim
14421      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
14422      */
14423     shim:false,
14424     // inherit
14425     shadow:"frame",
14426     /**
14427      * @cfg {Number} maxWidth
14428      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
14429      * the containing tree element's size, it will be automatically limited for you to the container width, taking
14430      * scroll and client offsets into account prior to each edit.
14431      */
14432     maxWidth: 250,
14433
14434     editDelay : 350,
14435
14436     // private
14437     fitToTree : function(ed, el){
14438         var td = this.tree.getTreeEl().dom, nd = el.dom;
14439         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
14440             td.scrollLeft = nd.offsetLeft;
14441         }
14442         var w = Math.min(
14443                 this.maxWidth,
14444                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
14445         this.setSize(w, '');
14446         
14447         return this.fireEvent('beforenodeedit', this, this.editNode);
14448         
14449     },
14450
14451     // private
14452     triggerEdit : function(node){
14453         this.completeEdit();
14454         this.editNode = node;
14455         this.startEdit(node.ui.textNode, node.text);
14456     },
14457
14458     // private
14459     bindScroll : function(){
14460         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
14461     },
14462
14463     // private
14464     beforeNodeClick : function(node, e){
14465         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
14466         this.lastClick = new Date();
14467         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
14468             e.stopEvent();
14469             this.triggerEdit(node);
14470             return false;
14471         }
14472         return true;
14473     },
14474
14475     // private
14476     updateNode : function(ed, value){
14477         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
14478         this.editNode.setText(value);
14479     },
14480
14481     // private
14482     onHide : function(){
14483         Roo.tree.TreeEditor.superclass.onHide.call(this);
14484         if(this.editNode){
14485             this.editNode.ui.focus();
14486         }
14487     },
14488
14489     // private
14490     onSpecialKey : function(field, e){
14491         var k = e.getKey();
14492         if(k == e.ESC){
14493             e.stopEvent();
14494             this.cancelEdit();
14495         }else if(k == e.ENTER && !e.hasModifier()){
14496             e.stopEvent();
14497             this.completeEdit();
14498         }
14499     }
14500 });//<Script type="text/javascript">
14501 /*
14502  * Based on:
14503  * Ext JS Library 1.1.1
14504  * Copyright(c) 2006-2007, Ext JS, LLC.
14505  *
14506  * Originally Released Under LGPL - original licence link has changed is not relivant.
14507  *
14508  * Fork - LGPL
14509  * <script type="text/javascript">
14510  */
14511  
14512 /**
14513  * Not documented??? - probably should be...
14514  */
14515
14516 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
14517     //focus: Roo.emptyFn, // prevent odd scrolling behavior
14518     
14519     renderElements : function(n, a, targetNode, bulkRender){
14520         //consel.log("renderElements?");
14521         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
14522
14523         var t = n.getOwnerTree();
14524         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
14525         
14526         var cols = t.columns;
14527         var bw = t.borderWidth;
14528         var c = cols[0];
14529         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
14530          var cb = typeof a.checked == "boolean";
14531         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14532         var colcls = 'x-t-' + tid + '-c0';
14533         var buf = [
14534             '<li class="x-tree-node">',
14535             
14536                 
14537                 '<div class="x-tree-node-el ', a.cls,'">',
14538                     // extran...
14539                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
14540                 
14541                 
14542                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
14543                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
14544                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
14545                            (a.icon ? ' x-tree-node-inline-icon' : ''),
14546                            (a.iconCls ? ' '+a.iconCls : ''),
14547                            '" unselectable="on" />',
14548                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
14549                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
14550                              
14551                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14552                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
14553                             '<span unselectable="on" qtip="' + tx + '">',
14554                              tx,
14555                              '</span></a>' ,
14556                     '</div>',
14557                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14558                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
14559                  ];
14560         for(var i = 1, len = cols.length; i < len; i++){
14561             c = cols[i];
14562             colcls = 'x-t-' + tid + '-c' +i;
14563             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14564             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
14565                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
14566                       "</div>");
14567          }
14568          
14569          buf.push(
14570             '</a>',
14571             '<div class="x-clear"></div></div>',
14572             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
14573             "</li>");
14574         
14575         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
14576             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
14577                                 n.nextSibling.ui.getEl(), buf.join(""));
14578         }else{
14579             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
14580         }
14581         var el = this.wrap.firstChild;
14582         this.elRow = el;
14583         this.elNode = el.firstChild;
14584         this.ranchor = el.childNodes[1];
14585         this.ctNode = this.wrap.childNodes[1];
14586         var cs = el.firstChild.childNodes;
14587         this.indentNode = cs[0];
14588         this.ecNode = cs[1];
14589         this.iconNode = cs[2];
14590         var index = 3;
14591         if(cb){
14592             this.checkbox = cs[3];
14593             index++;
14594         }
14595         this.anchor = cs[index];
14596         
14597         this.textNode = cs[index].firstChild;
14598         
14599         //el.on("click", this.onClick, this);
14600         //el.on("dblclick", this.onDblClick, this);
14601         
14602         
14603        // console.log(this);
14604     },
14605     initEvents : function(){
14606         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
14607         
14608             
14609         var a = this.ranchor;
14610
14611         var el = Roo.get(a);
14612
14613         if(Roo.isOpera){ // opera render bug ignores the CSS
14614             el.setStyle("text-decoration", "none");
14615         }
14616
14617         el.on("click", this.onClick, this);
14618         el.on("dblclick", this.onDblClick, this);
14619         el.on("contextmenu", this.onContextMenu, this);
14620         
14621     },
14622     
14623     /*onSelectedChange : function(state){
14624         if(state){
14625             this.focus();
14626             this.addClass("x-tree-selected");
14627         }else{
14628             //this.blur();
14629             this.removeClass("x-tree-selected");
14630         }
14631     },*/
14632     addClass : function(cls){
14633         if(this.elRow){
14634             Roo.fly(this.elRow).addClass(cls);
14635         }
14636         
14637     },
14638     
14639     
14640     removeClass : function(cls){
14641         if(this.elRow){
14642             Roo.fly(this.elRow).removeClass(cls);
14643         }
14644     }
14645
14646     
14647     
14648 });//<Script type="text/javascript">
14649
14650 /*
14651  * Based on:
14652  * Ext JS Library 1.1.1
14653  * Copyright(c) 2006-2007, Ext JS, LLC.
14654  *
14655  * Originally Released Under LGPL - original licence link has changed is not relivant.
14656  *
14657  * Fork - LGPL
14658  * <script type="text/javascript">
14659  */
14660  
14661
14662 /**
14663  * @class Roo.tree.ColumnTree
14664  * @extends Roo.data.TreePanel
14665  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
14666  * @cfg {int} borderWidth  compined right/left border allowance
14667  * @constructor
14668  * @param {String/HTMLElement/Element} el The container element
14669  * @param {Object} config
14670  */
14671 Roo.tree.ColumnTree =  function(el, config)
14672 {
14673    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
14674    this.addEvents({
14675         /**
14676         * @event resize
14677         * Fire this event on a container when it resizes
14678         * @param {int} w Width
14679         * @param {int} h Height
14680         */
14681        "resize" : true
14682     });
14683     this.on('resize', this.onResize, this);
14684 };
14685
14686 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
14687     //lines:false,
14688     
14689     
14690     borderWidth: Roo.isBorderBox ? 0 : 2, 
14691     headEls : false,
14692     
14693     render : function(){
14694         // add the header.....
14695        
14696         Roo.tree.ColumnTree.superclass.render.apply(this);
14697         
14698         this.el.addClass('x-column-tree');
14699         
14700         this.headers = this.el.createChild(
14701             {cls:'x-tree-headers'},this.innerCt.dom);
14702    
14703         var cols = this.columns, c;
14704         var totalWidth = 0;
14705         this.headEls = [];
14706         var  len = cols.length;
14707         for(var i = 0; i < len; i++){
14708              c = cols[i];
14709              totalWidth += c.width;
14710             this.headEls.push(this.headers.createChild({
14711                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
14712                  cn: {
14713                      cls:'x-tree-hd-text',
14714                      html: c.header
14715                  },
14716                  style:'width:'+(c.width-this.borderWidth)+'px;'
14717              }));
14718         }
14719         this.headers.createChild({cls:'x-clear'});
14720         // prevent floats from wrapping when clipped
14721         this.headers.setWidth(totalWidth);
14722         //this.innerCt.setWidth(totalWidth);
14723         this.innerCt.setStyle({ overflow: 'auto' });
14724         this.onResize(this.width, this.height);
14725              
14726         
14727     },
14728     onResize : function(w,h)
14729     {
14730         this.height = h;
14731         this.width = w;
14732         // resize cols..
14733         this.innerCt.setWidth(this.width);
14734         this.innerCt.setHeight(this.height-20);
14735         
14736         // headers...
14737         var cols = this.columns, c;
14738         var totalWidth = 0;
14739         var expEl = false;
14740         var len = cols.length;
14741         for(var i = 0; i < len; i++){
14742             c = cols[i];
14743             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
14744                 // it's the expander..
14745                 expEl  = this.headEls[i];
14746                 continue;
14747             }
14748             totalWidth += c.width;
14749             
14750         }
14751         if (expEl) {
14752             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
14753         }
14754         this.headers.setWidth(w-20);
14755
14756         
14757         
14758         
14759     }
14760 });
14761 /*
14762  * Based on:
14763  * Ext JS Library 1.1.1
14764  * Copyright(c) 2006-2007, Ext JS, LLC.
14765  *
14766  * Originally Released Under LGPL - original licence link has changed is not relivant.
14767  *
14768  * Fork - LGPL
14769  * <script type="text/javascript">
14770  */
14771  
14772 /**
14773  * @class Roo.menu.Menu
14774  * @extends Roo.util.Observable
14775  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
14776  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
14777  * @constructor
14778  * Creates a new Menu
14779  * @param {Object} config Configuration options
14780  */
14781 Roo.menu.Menu = function(config){
14782     
14783     Roo.menu.Menu.superclass.constructor.call(this, config);
14784     
14785     this.id = this.id || Roo.id();
14786     this.addEvents({
14787         /**
14788          * @event beforeshow
14789          * Fires before this menu is displayed
14790          * @param {Roo.menu.Menu} this
14791          */
14792         beforeshow : true,
14793         /**
14794          * @event beforehide
14795          * Fires before this menu is hidden
14796          * @param {Roo.menu.Menu} this
14797          */
14798         beforehide : true,
14799         /**
14800          * @event show
14801          * Fires after this menu is displayed
14802          * @param {Roo.menu.Menu} this
14803          */
14804         show : true,
14805         /**
14806          * @event hide
14807          * Fires after this menu is hidden
14808          * @param {Roo.menu.Menu} this
14809          */
14810         hide : true,
14811         /**
14812          * @event click
14813          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
14814          * @param {Roo.menu.Menu} this
14815          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14816          * @param {Roo.EventObject} e
14817          */
14818         click : true,
14819         /**
14820          * @event mouseover
14821          * Fires when the mouse is hovering over this menu
14822          * @param {Roo.menu.Menu} this
14823          * @param {Roo.EventObject} e
14824          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14825          */
14826         mouseover : true,
14827         /**
14828          * @event mouseout
14829          * Fires when the mouse exits this menu
14830          * @param {Roo.menu.Menu} this
14831          * @param {Roo.EventObject} e
14832          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14833          */
14834         mouseout : true,
14835         /**
14836          * @event itemclick
14837          * Fires when a menu item contained in this menu is clicked
14838          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
14839          * @param {Roo.EventObject} e
14840          */
14841         itemclick: true
14842     });
14843     if (this.registerMenu) {
14844         Roo.menu.MenuMgr.register(this);
14845     }
14846     
14847     var mis = this.items;
14848     this.items = new Roo.util.MixedCollection();
14849     if(mis){
14850         this.add.apply(this, mis);
14851     }
14852 };
14853
14854 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
14855     /**
14856      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
14857      */
14858     minWidth : 120,
14859     /**
14860      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
14861      * for bottom-right shadow (defaults to "sides")
14862      */
14863     shadow : "sides",
14864     /**
14865      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
14866      * this menu (defaults to "tl-tr?")
14867      */
14868     subMenuAlign : "tl-tr?",
14869     /**
14870      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
14871      * relative to its element of origin (defaults to "tl-bl?")
14872      */
14873     defaultAlign : "tl-bl?",
14874     /**
14875      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
14876      */
14877     allowOtherMenus : false,
14878     /**
14879      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
14880      */
14881     registerMenu : true,
14882
14883     hidden:true,
14884
14885     // private
14886     render : function(){
14887         if(this.el){
14888             return;
14889         }
14890         var el = this.el = new Roo.Layer({
14891             cls: "x-menu",
14892             shadow:this.shadow,
14893             constrain: false,
14894             parentEl: this.parentEl || document.body,
14895             zindex:15000
14896         });
14897
14898         this.keyNav = new Roo.menu.MenuNav(this);
14899
14900         if(this.plain){
14901             el.addClass("x-menu-plain");
14902         }
14903         if(this.cls){
14904             el.addClass(this.cls);
14905         }
14906         // generic focus element
14907         this.focusEl = el.createChild({
14908             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
14909         });
14910         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
14911         //disabling touch- as it's causing issues ..
14912         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
14913         ul.on('click'   , this.onClick, this);
14914         
14915         
14916         ul.on("mouseover", this.onMouseOver, this);
14917         ul.on("mouseout", this.onMouseOut, this);
14918         this.items.each(function(item){
14919             if (item.hidden) {
14920                 return;
14921             }
14922             
14923             var li = document.createElement("li");
14924             li.className = "x-menu-list-item";
14925             ul.dom.appendChild(li);
14926             item.render(li, this);
14927         }, this);
14928         this.ul = ul;
14929         this.autoWidth();
14930     },
14931
14932     // private
14933     autoWidth : function(){
14934         var el = this.el, ul = this.ul;
14935         if(!el){
14936             return;
14937         }
14938         var w = this.width;
14939         if(w){
14940             el.setWidth(w);
14941         }else if(Roo.isIE){
14942             el.setWidth(this.minWidth);
14943             var t = el.dom.offsetWidth; // force recalc
14944             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
14945         }
14946     },
14947
14948     // private
14949     delayAutoWidth : function(){
14950         if(this.rendered){
14951             if(!this.awTask){
14952                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
14953             }
14954             this.awTask.delay(20);
14955         }
14956     },
14957
14958     // private
14959     findTargetItem : function(e){
14960         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
14961         if(t && t.menuItemId){
14962             return this.items.get(t.menuItemId);
14963         }
14964     },
14965
14966     // private
14967     onClick : function(e){
14968         Roo.log("menu.onClick");
14969         var t = this.findTargetItem(e);
14970         if(!t){
14971             return;
14972         }
14973         Roo.log(e);
14974         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
14975             if(t == this.activeItem && t.shouldDeactivate(e)){
14976                 this.activeItem.deactivate();
14977                 delete this.activeItem;
14978                 return;
14979             }
14980             if(t.canActivate){
14981                 this.setActiveItem(t, true);
14982             }
14983             return;
14984             
14985             
14986         }
14987         
14988         t.onClick(e);
14989         this.fireEvent("click", this, t, e);
14990     },
14991
14992     // private
14993     setActiveItem : function(item, autoExpand){
14994         if(item != this.activeItem){
14995             if(this.activeItem){
14996                 this.activeItem.deactivate();
14997             }
14998             this.activeItem = item;
14999             item.activate(autoExpand);
15000         }else if(autoExpand){
15001             item.expandMenu();
15002         }
15003     },
15004
15005     // private
15006     tryActivate : function(start, step){
15007         var items = this.items;
15008         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
15009             var item = items.get(i);
15010             if(!item.disabled && item.canActivate){
15011                 this.setActiveItem(item, false);
15012                 return item;
15013             }
15014         }
15015         return false;
15016     },
15017
15018     // private
15019     onMouseOver : function(e){
15020         var t;
15021         if(t = this.findTargetItem(e)){
15022             if(t.canActivate && !t.disabled){
15023                 this.setActiveItem(t, true);
15024             }
15025         }
15026         this.fireEvent("mouseover", this, e, t);
15027     },
15028
15029     // private
15030     onMouseOut : function(e){
15031         var t;
15032         if(t = this.findTargetItem(e)){
15033             if(t == this.activeItem && t.shouldDeactivate(e)){
15034                 this.activeItem.deactivate();
15035                 delete this.activeItem;
15036             }
15037         }
15038         this.fireEvent("mouseout", this, e, t);
15039     },
15040
15041     /**
15042      * Read-only.  Returns true if the menu is currently displayed, else false.
15043      * @type Boolean
15044      */
15045     isVisible : function(){
15046         return this.el && !this.hidden;
15047     },
15048
15049     /**
15050      * Displays this menu relative to another element
15051      * @param {String/HTMLElement/Roo.Element} element The element to align to
15052      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
15053      * the element (defaults to this.defaultAlign)
15054      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
15055      */
15056     show : function(el, pos, parentMenu){
15057         this.parentMenu = parentMenu;
15058         if(!this.el){
15059             this.render();
15060         }
15061         this.fireEvent("beforeshow", this);
15062         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
15063     },
15064
15065     /**
15066      * Displays this menu at a specific xy position
15067      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
15068      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
15069      */
15070     showAt : function(xy, parentMenu, /* private: */_e){
15071         this.parentMenu = parentMenu;
15072         if(!this.el){
15073             this.render();
15074         }
15075         if(_e !== false){
15076             this.fireEvent("beforeshow", this);
15077             xy = this.el.adjustForConstraints(xy);
15078         }
15079         this.el.setXY(xy);
15080         this.el.show();
15081         this.hidden = false;
15082         this.focus();
15083         this.fireEvent("show", this);
15084     },
15085
15086     focus : function(){
15087         if(!this.hidden){
15088             this.doFocus.defer(50, this);
15089         }
15090     },
15091
15092     doFocus : function(){
15093         if(!this.hidden){
15094             this.focusEl.focus();
15095         }
15096     },
15097
15098     /**
15099      * Hides this menu and optionally all parent menus
15100      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
15101      */
15102     hide : function(deep){
15103         if(this.el && this.isVisible()){
15104             this.fireEvent("beforehide", this);
15105             if(this.activeItem){
15106                 this.activeItem.deactivate();
15107                 this.activeItem = null;
15108             }
15109             this.el.hide();
15110             this.hidden = true;
15111             this.fireEvent("hide", this);
15112         }
15113         if(deep === true && this.parentMenu){
15114             this.parentMenu.hide(true);
15115         }
15116     },
15117
15118     /**
15119      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
15120      * Any of the following are valid:
15121      * <ul>
15122      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
15123      * <li>An HTMLElement object which will be converted to a menu item</li>
15124      * <li>A menu item config object that will be created as a new menu item</li>
15125      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
15126      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
15127      * </ul>
15128      * Usage:
15129      * <pre><code>
15130 // Create the menu
15131 var menu = new Roo.menu.Menu();
15132
15133 // Create a menu item to add by reference
15134 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
15135
15136 // Add a bunch of items at once using different methods.
15137 // Only the last item added will be returned.
15138 var item = menu.add(
15139     menuItem,                // add existing item by ref
15140     'Dynamic Item',          // new TextItem
15141     '-',                     // new separator
15142     { text: 'Config Item' }  // new item by config
15143 );
15144 </code></pre>
15145      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
15146      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
15147      */
15148     add : function(){
15149         var a = arguments, l = a.length, item;
15150         for(var i = 0; i < l; i++){
15151             var el = a[i];
15152             if ((typeof(el) == "object") && el.xtype && el.xns) {
15153                 el = Roo.factory(el, Roo.menu);
15154             }
15155             
15156             if(el.render){ // some kind of Item
15157                 item = this.addItem(el);
15158             }else if(typeof el == "string"){ // string
15159                 if(el == "separator" || el == "-"){
15160                     item = this.addSeparator();
15161                 }else{
15162                     item = this.addText(el);
15163                 }
15164             }else if(el.tagName || el.el){ // element
15165                 item = this.addElement(el);
15166             }else if(typeof el == "object"){ // must be menu item config?
15167                 item = this.addMenuItem(el);
15168             }
15169         }
15170         return item;
15171     },
15172
15173     /**
15174      * Returns this menu's underlying {@link Roo.Element} object
15175      * @return {Roo.Element} The element
15176      */
15177     getEl : function(){
15178         if(!this.el){
15179             this.render();
15180         }
15181         return this.el;
15182     },
15183
15184     /**
15185      * Adds a separator bar to the menu
15186      * @return {Roo.menu.Item} The menu item that was added
15187      */
15188     addSeparator : function(){
15189         return this.addItem(new Roo.menu.Separator());
15190     },
15191
15192     /**
15193      * Adds an {@link Roo.Element} object to the menu
15194      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
15195      * @return {Roo.menu.Item} The menu item that was added
15196      */
15197     addElement : function(el){
15198         return this.addItem(new Roo.menu.BaseItem(el));
15199     },
15200
15201     /**
15202      * Adds an existing object based on {@link Roo.menu.Item} to the menu
15203      * @param {Roo.menu.Item} item The menu item to add
15204      * @return {Roo.menu.Item} The menu item that was added
15205      */
15206     addItem : function(item){
15207         this.items.add(item);
15208         if(this.ul){
15209             var li = document.createElement("li");
15210             li.className = "x-menu-list-item";
15211             this.ul.dom.appendChild(li);
15212             item.render(li, this);
15213             this.delayAutoWidth();
15214         }
15215         return item;
15216     },
15217
15218     /**
15219      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
15220      * @param {Object} config A MenuItem config object
15221      * @return {Roo.menu.Item} The menu item that was added
15222      */
15223     addMenuItem : function(config){
15224         if(!(config instanceof Roo.menu.Item)){
15225             if(typeof config.checked == "boolean"){ // must be check menu item config?
15226                 config = new Roo.menu.CheckItem(config);
15227             }else{
15228                 config = new Roo.menu.Item(config);
15229             }
15230         }
15231         return this.addItem(config);
15232     },
15233
15234     /**
15235      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
15236      * @param {String} text The text to display in the menu item
15237      * @return {Roo.menu.Item} The menu item that was added
15238      */
15239     addText : function(text){
15240         return this.addItem(new Roo.menu.TextItem({ text : text }));
15241     },
15242
15243     /**
15244      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
15245      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
15246      * @param {Roo.menu.Item} item The menu item to add
15247      * @return {Roo.menu.Item} The menu item that was added
15248      */
15249     insert : function(index, item){
15250         this.items.insert(index, item);
15251         if(this.ul){
15252             var li = document.createElement("li");
15253             li.className = "x-menu-list-item";
15254             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
15255             item.render(li, this);
15256             this.delayAutoWidth();
15257         }
15258         return item;
15259     },
15260
15261     /**
15262      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
15263      * @param {Roo.menu.Item} item The menu item to remove
15264      */
15265     remove : function(item){
15266         this.items.removeKey(item.id);
15267         item.destroy();
15268     },
15269
15270     /**
15271      * Removes and destroys all items in the menu
15272      */
15273     removeAll : function(){
15274         var f;
15275         while(f = this.items.first()){
15276             this.remove(f);
15277         }
15278     }
15279 });
15280
15281 // MenuNav is a private utility class used internally by the Menu
15282 Roo.menu.MenuNav = function(menu){
15283     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
15284     this.scope = this.menu = menu;
15285 };
15286
15287 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
15288     doRelay : function(e, h){
15289         var k = e.getKey();
15290         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
15291             this.menu.tryActivate(0, 1);
15292             return false;
15293         }
15294         return h.call(this.scope || this, e, this.menu);
15295     },
15296
15297     up : function(e, m){
15298         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
15299             m.tryActivate(m.items.length-1, -1);
15300         }
15301     },
15302
15303     down : function(e, m){
15304         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
15305             m.tryActivate(0, 1);
15306         }
15307     },
15308
15309     right : function(e, m){
15310         if(m.activeItem){
15311             m.activeItem.expandMenu(true);
15312         }
15313     },
15314
15315     left : function(e, m){
15316         m.hide();
15317         if(m.parentMenu && m.parentMenu.activeItem){
15318             m.parentMenu.activeItem.activate();
15319         }
15320     },
15321
15322     enter : function(e, m){
15323         if(m.activeItem){
15324             e.stopPropagation();
15325             m.activeItem.onClick(e);
15326             m.fireEvent("click", this, m.activeItem);
15327             return true;
15328         }
15329     }
15330 });/*
15331  * Based on:
15332  * Ext JS Library 1.1.1
15333  * Copyright(c) 2006-2007, Ext JS, LLC.
15334  *
15335  * Originally Released Under LGPL - original licence link has changed is not relivant.
15336  *
15337  * Fork - LGPL
15338  * <script type="text/javascript">
15339  */
15340  
15341 /**
15342  * @class Roo.menu.MenuMgr
15343  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
15344  * @singleton
15345  */
15346 Roo.menu.MenuMgr = function(){
15347    var menus, active, groups = {}, attached = false, lastShow = new Date();
15348
15349    // private - called when first menu is created
15350    function init(){
15351        menus = {};
15352        active = new Roo.util.MixedCollection();
15353        Roo.get(document).addKeyListener(27, function(){
15354            if(active.length > 0){
15355                hideAll();
15356            }
15357        });
15358    }
15359
15360    // private
15361    function hideAll(){
15362        if(active && active.length > 0){
15363            var c = active.clone();
15364            c.each(function(m){
15365                m.hide();
15366            });
15367        }
15368    }
15369
15370    // private
15371    function onHide(m){
15372        active.remove(m);
15373        if(active.length < 1){
15374            Roo.get(document).un("mousedown", onMouseDown);
15375            attached = false;
15376        }
15377    }
15378
15379    // private
15380    function onShow(m){
15381        var last = active.last();
15382        lastShow = new Date();
15383        active.add(m);
15384        if(!attached){
15385            Roo.get(document).on("mousedown", onMouseDown);
15386            attached = true;
15387        }
15388        if(m.parentMenu){
15389           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
15390           m.parentMenu.activeChild = m;
15391        }else if(last && last.isVisible()){
15392           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
15393        }
15394    }
15395
15396    // private
15397    function onBeforeHide(m){
15398        if(m.activeChild){
15399            m.activeChild.hide();
15400        }
15401        if(m.autoHideTimer){
15402            clearTimeout(m.autoHideTimer);
15403            delete m.autoHideTimer;
15404        }
15405    }
15406
15407    // private
15408    function onBeforeShow(m){
15409        var pm = m.parentMenu;
15410        if(!pm && !m.allowOtherMenus){
15411            hideAll();
15412        }else if(pm && pm.activeChild && active != m){
15413            pm.activeChild.hide();
15414        }
15415    }
15416
15417    // private
15418    function onMouseDown(e){
15419        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
15420            hideAll();
15421        }
15422    }
15423
15424    // private
15425    function onBeforeCheck(mi, state){
15426        if(state){
15427            var g = groups[mi.group];
15428            for(var i = 0, l = g.length; i < l; i++){
15429                if(g[i] != mi){
15430                    g[i].setChecked(false);
15431                }
15432            }
15433        }
15434    }
15435
15436    return {
15437
15438        /**
15439         * Hides all menus that are currently visible
15440         */
15441        hideAll : function(){
15442             hideAll();  
15443        },
15444
15445        // private
15446        register : function(menu){
15447            if(!menus){
15448                init();
15449            }
15450            menus[menu.id] = menu;
15451            menu.on("beforehide", onBeforeHide);
15452            menu.on("hide", onHide);
15453            menu.on("beforeshow", onBeforeShow);
15454            menu.on("show", onShow);
15455            var g = menu.group;
15456            if(g && menu.events["checkchange"]){
15457                if(!groups[g]){
15458                    groups[g] = [];
15459                }
15460                groups[g].push(menu);
15461                menu.on("checkchange", onCheck);
15462            }
15463        },
15464
15465         /**
15466          * Returns a {@link Roo.menu.Menu} object
15467          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
15468          * be used to generate and return a new Menu instance.
15469          */
15470        get : function(menu){
15471            if(typeof menu == "string"){ // menu id
15472                return menus[menu];
15473            }else if(menu.events){  // menu instance
15474                return menu;
15475            }else if(typeof menu.length == 'number'){ // array of menu items?
15476                return new Roo.menu.Menu({items:menu});
15477            }else{ // otherwise, must be a config
15478                return new Roo.menu.Menu(menu);
15479            }
15480        },
15481
15482        // private
15483        unregister : function(menu){
15484            delete menus[menu.id];
15485            menu.un("beforehide", onBeforeHide);
15486            menu.un("hide", onHide);
15487            menu.un("beforeshow", onBeforeShow);
15488            menu.un("show", onShow);
15489            var g = menu.group;
15490            if(g && menu.events["checkchange"]){
15491                groups[g].remove(menu);
15492                menu.un("checkchange", onCheck);
15493            }
15494        },
15495
15496        // private
15497        registerCheckable : function(menuItem){
15498            var g = menuItem.group;
15499            if(g){
15500                if(!groups[g]){
15501                    groups[g] = [];
15502                }
15503                groups[g].push(menuItem);
15504                menuItem.on("beforecheckchange", onBeforeCheck);
15505            }
15506        },
15507
15508        // private
15509        unregisterCheckable : function(menuItem){
15510            var g = menuItem.group;
15511            if(g){
15512                groups[g].remove(menuItem);
15513                menuItem.un("beforecheckchange", onBeforeCheck);
15514            }
15515        }
15516    };
15517 }();/*
15518  * Based on:
15519  * Ext JS Library 1.1.1
15520  * Copyright(c) 2006-2007, Ext JS, LLC.
15521  *
15522  * Originally Released Under LGPL - original licence link has changed is not relivant.
15523  *
15524  * Fork - LGPL
15525  * <script type="text/javascript">
15526  */
15527  
15528
15529 /**
15530  * @class Roo.menu.BaseItem
15531  * @extends Roo.Component
15532  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
15533  * management and base configuration options shared by all menu components.
15534  * @constructor
15535  * Creates a new BaseItem
15536  * @param {Object} config Configuration options
15537  */
15538 Roo.menu.BaseItem = function(config){
15539     Roo.menu.BaseItem.superclass.constructor.call(this, config);
15540
15541     this.addEvents({
15542         /**
15543          * @event click
15544          * Fires when this item is clicked
15545          * @param {Roo.menu.BaseItem} this
15546          * @param {Roo.EventObject} e
15547          */
15548         click: true,
15549         /**
15550          * @event activate
15551          * Fires when this item is activated
15552          * @param {Roo.menu.BaseItem} this
15553          */
15554         activate : true,
15555         /**
15556          * @event deactivate
15557          * Fires when this item is deactivated
15558          * @param {Roo.menu.BaseItem} this
15559          */
15560         deactivate : true
15561     });
15562
15563     if(this.handler){
15564         this.on("click", this.handler, this.scope, true);
15565     }
15566 };
15567
15568 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
15569     /**
15570      * @cfg {Function} handler
15571      * A function that will handle the click event of this menu item (defaults to undefined)
15572      */
15573     /**
15574      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
15575      */
15576     canActivate : false,
15577     
15578      /**
15579      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
15580      */
15581     hidden: false,
15582     
15583     /**
15584      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
15585      */
15586     activeClass : "x-menu-item-active",
15587     /**
15588      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
15589      */
15590     hideOnClick : true,
15591     /**
15592      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
15593      */
15594     hideDelay : 100,
15595
15596     // private
15597     ctype: "Roo.menu.BaseItem",
15598
15599     // private
15600     actionMode : "container",
15601
15602     // private
15603     render : function(container, parentMenu){
15604         this.parentMenu = parentMenu;
15605         Roo.menu.BaseItem.superclass.render.call(this, container);
15606         this.container.menuItemId = this.id;
15607     },
15608
15609     // private
15610     onRender : function(container, position){
15611         this.el = Roo.get(this.el);
15612         container.dom.appendChild(this.el.dom);
15613     },
15614
15615     // private
15616     onClick : function(e){
15617         if(!this.disabled && this.fireEvent("click", this, e) !== false
15618                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
15619             this.handleClick(e);
15620         }else{
15621             e.stopEvent();
15622         }
15623     },
15624
15625     // private
15626     activate : function(){
15627         if(this.disabled){
15628             return false;
15629         }
15630         var li = this.container;
15631         li.addClass(this.activeClass);
15632         this.region = li.getRegion().adjust(2, 2, -2, -2);
15633         this.fireEvent("activate", this);
15634         return true;
15635     },
15636
15637     // private
15638     deactivate : function(){
15639         this.container.removeClass(this.activeClass);
15640         this.fireEvent("deactivate", this);
15641     },
15642
15643     // private
15644     shouldDeactivate : function(e){
15645         return !this.region || !this.region.contains(e.getPoint());
15646     },
15647
15648     // private
15649     handleClick : function(e){
15650         if(this.hideOnClick){
15651             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
15652         }
15653     },
15654
15655     // private
15656     expandMenu : function(autoActivate){
15657         // do nothing
15658     },
15659
15660     // private
15661     hideMenu : function(){
15662         // do nothing
15663     }
15664 });/*
15665  * Based on:
15666  * Ext JS Library 1.1.1
15667  * Copyright(c) 2006-2007, Ext JS, LLC.
15668  *
15669  * Originally Released Under LGPL - original licence link has changed is not relivant.
15670  *
15671  * Fork - LGPL
15672  * <script type="text/javascript">
15673  */
15674  
15675 /**
15676  * @class Roo.menu.Adapter
15677  * @extends Roo.menu.BaseItem
15678  * 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.
15679  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
15680  * @constructor
15681  * Creates a new Adapter
15682  * @param {Object} config Configuration options
15683  */
15684 Roo.menu.Adapter = function(component, config){
15685     Roo.menu.Adapter.superclass.constructor.call(this, config);
15686     this.component = component;
15687 };
15688 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
15689     // private
15690     canActivate : true,
15691
15692     // private
15693     onRender : function(container, position){
15694         this.component.render(container);
15695         this.el = this.component.getEl();
15696     },
15697
15698     // private
15699     activate : function(){
15700         if(this.disabled){
15701             return false;
15702         }
15703         this.component.focus();
15704         this.fireEvent("activate", this);
15705         return true;
15706     },
15707
15708     // private
15709     deactivate : function(){
15710         this.fireEvent("deactivate", this);
15711     },
15712
15713     // private
15714     disable : function(){
15715         this.component.disable();
15716         Roo.menu.Adapter.superclass.disable.call(this);
15717     },
15718
15719     // private
15720     enable : function(){
15721         this.component.enable();
15722         Roo.menu.Adapter.superclass.enable.call(this);
15723     }
15724 });/*
15725  * Based on:
15726  * Ext JS Library 1.1.1
15727  * Copyright(c) 2006-2007, Ext JS, LLC.
15728  *
15729  * Originally Released Under LGPL - original licence link has changed is not relivant.
15730  *
15731  * Fork - LGPL
15732  * <script type="text/javascript">
15733  */
15734
15735 /**
15736  * @class Roo.menu.TextItem
15737  * @extends Roo.menu.BaseItem
15738  * Adds a static text string to a menu, usually used as either a heading or group separator.
15739  * Note: old style constructor with text is still supported.
15740  * 
15741  * @constructor
15742  * Creates a new TextItem
15743  * @param {Object} cfg Configuration
15744  */
15745 Roo.menu.TextItem = function(cfg){
15746     if (typeof(cfg) == 'string') {
15747         this.text = cfg;
15748     } else {
15749         Roo.apply(this,cfg);
15750     }
15751     
15752     Roo.menu.TextItem.superclass.constructor.call(this);
15753 };
15754
15755 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
15756     /**
15757      * @cfg {Boolean} text Text to show on item.
15758      */
15759     text : '',
15760     
15761     /**
15762      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15763      */
15764     hideOnClick : false,
15765     /**
15766      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
15767      */
15768     itemCls : "x-menu-text",
15769
15770     // private
15771     onRender : function(){
15772         var s = document.createElement("span");
15773         s.className = this.itemCls;
15774         s.innerHTML = this.text;
15775         this.el = s;
15776         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
15777     }
15778 });/*
15779  * Based on:
15780  * Ext JS Library 1.1.1
15781  * Copyright(c) 2006-2007, Ext JS, LLC.
15782  *
15783  * Originally Released Under LGPL - original licence link has changed is not relivant.
15784  *
15785  * Fork - LGPL
15786  * <script type="text/javascript">
15787  */
15788
15789 /**
15790  * @class Roo.menu.Separator
15791  * @extends Roo.menu.BaseItem
15792  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
15793  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
15794  * @constructor
15795  * @param {Object} config Configuration options
15796  */
15797 Roo.menu.Separator = function(config){
15798     Roo.menu.Separator.superclass.constructor.call(this, config);
15799 };
15800
15801 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
15802     /**
15803      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
15804      */
15805     itemCls : "x-menu-sep",
15806     /**
15807      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15808      */
15809     hideOnClick : false,
15810
15811     // private
15812     onRender : function(li){
15813         var s = document.createElement("span");
15814         s.className = this.itemCls;
15815         s.innerHTML = "&#160;";
15816         this.el = s;
15817         li.addClass("x-menu-sep-li");
15818         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
15819     }
15820 });/*
15821  * Based on:
15822  * Ext JS Library 1.1.1
15823  * Copyright(c) 2006-2007, Ext JS, LLC.
15824  *
15825  * Originally Released Under LGPL - original licence link has changed is not relivant.
15826  *
15827  * Fork - LGPL
15828  * <script type="text/javascript">
15829  */
15830 /**
15831  * @class Roo.menu.Item
15832  * @extends Roo.menu.BaseItem
15833  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
15834  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
15835  * activation and click handling.
15836  * @constructor
15837  * Creates a new Item
15838  * @param {Object} config Configuration options
15839  */
15840 Roo.menu.Item = function(config){
15841     Roo.menu.Item.superclass.constructor.call(this, config);
15842     if(this.menu){
15843         this.menu = Roo.menu.MenuMgr.get(this.menu);
15844     }
15845 };
15846 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
15847     
15848     /**
15849      * @cfg {String} text
15850      * The text to show on the menu item.
15851      */
15852     text: '',
15853      /**
15854      * @cfg {String} HTML to render in menu
15855      * The text to show on the menu item (HTML version).
15856      */
15857     html: '',
15858     /**
15859      * @cfg {String} icon
15860      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
15861      */
15862     icon: undefined,
15863     /**
15864      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
15865      */
15866     itemCls : "x-menu-item",
15867     /**
15868      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
15869      */
15870     canActivate : true,
15871     /**
15872      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
15873      */
15874     showDelay: 200,
15875     // doc'd in BaseItem
15876     hideDelay: 200,
15877
15878     // private
15879     ctype: "Roo.menu.Item",
15880     
15881     // private
15882     onRender : function(container, position){
15883         var el = document.createElement("a");
15884         el.hideFocus = true;
15885         el.unselectable = "on";
15886         el.href = this.href || "#";
15887         if(this.hrefTarget){
15888             el.target = this.hrefTarget;
15889         }
15890         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
15891         
15892         var html = this.html.length ? this.html  : String.format('{0}',this.text);
15893         
15894         el.innerHTML = String.format(
15895                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
15896                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
15897         this.el = el;
15898         Roo.menu.Item.superclass.onRender.call(this, container, position);
15899     },
15900
15901     /**
15902      * Sets the text to display in this menu item
15903      * @param {String} text The text to display
15904      * @param {Boolean} isHTML true to indicate text is pure html.
15905      */
15906     setText : function(text, isHTML){
15907         if (isHTML) {
15908             this.html = text;
15909         } else {
15910             this.text = text;
15911             this.html = '';
15912         }
15913         if(this.rendered){
15914             var html = this.html.length ? this.html  : String.format('{0}',this.text);
15915      
15916             this.el.update(String.format(
15917                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
15918                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
15919             this.parentMenu.autoWidth();
15920         }
15921     },
15922
15923     // private
15924     handleClick : function(e){
15925         if(!this.href){ // if no link defined, stop the event automatically
15926             e.stopEvent();
15927         }
15928         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
15929     },
15930
15931     // private
15932     activate : function(autoExpand){
15933         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
15934             this.focus();
15935             if(autoExpand){
15936                 this.expandMenu();
15937             }
15938         }
15939         return true;
15940     },
15941
15942     // private
15943     shouldDeactivate : function(e){
15944         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
15945             if(this.menu && this.menu.isVisible()){
15946                 return !this.menu.getEl().getRegion().contains(e.getPoint());
15947             }
15948             return true;
15949         }
15950         return false;
15951     },
15952
15953     // private
15954     deactivate : function(){
15955         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
15956         this.hideMenu();
15957     },
15958
15959     // private
15960     expandMenu : function(autoActivate){
15961         if(!this.disabled && this.menu){
15962             clearTimeout(this.hideTimer);
15963             delete this.hideTimer;
15964             if(!this.menu.isVisible() && !this.showTimer){
15965                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
15966             }else if (this.menu.isVisible() && autoActivate){
15967                 this.menu.tryActivate(0, 1);
15968             }
15969         }
15970     },
15971
15972     // private
15973     deferExpand : function(autoActivate){
15974         delete this.showTimer;
15975         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
15976         if(autoActivate){
15977             this.menu.tryActivate(0, 1);
15978         }
15979     },
15980
15981     // private
15982     hideMenu : function(){
15983         clearTimeout(this.showTimer);
15984         delete this.showTimer;
15985         if(!this.hideTimer && this.menu && this.menu.isVisible()){
15986             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
15987         }
15988     },
15989
15990     // private
15991     deferHide : function(){
15992         delete this.hideTimer;
15993         this.menu.hide();
15994     }
15995 });/*
15996  * Based on:
15997  * Ext JS Library 1.1.1
15998  * Copyright(c) 2006-2007, Ext JS, LLC.
15999  *
16000  * Originally Released Under LGPL - original licence link has changed is not relivant.
16001  *
16002  * Fork - LGPL
16003  * <script type="text/javascript">
16004  */
16005  
16006 /**
16007  * @class Roo.menu.CheckItem
16008  * @extends Roo.menu.Item
16009  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
16010  * @constructor
16011  * Creates a new CheckItem
16012  * @param {Object} config Configuration options
16013  */
16014 Roo.menu.CheckItem = function(config){
16015     Roo.menu.CheckItem.superclass.constructor.call(this, config);
16016     this.addEvents({
16017         /**
16018          * @event beforecheckchange
16019          * Fires before the checked value is set, providing an opportunity to cancel if needed
16020          * @param {Roo.menu.CheckItem} this
16021          * @param {Boolean} checked The new checked value that will be set
16022          */
16023         "beforecheckchange" : true,
16024         /**
16025          * @event checkchange
16026          * Fires after the checked value has been set
16027          * @param {Roo.menu.CheckItem} this
16028          * @param {Boolean} checked The checked value that was set
16029          */
16030         "checkchange" : true
16031     });
16032     if(this.checkHandler){
16033         this.on('checkchange', this.checkHandler, this.scope);
16034     }
16035 };
16036 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
16037     /**
16038      * @cfg {String} group
16039      * All check items with the same group name will automatically be grouped into a single-select
16040      * radio button group (defaults to '')
16041      */
16042     /**
16043      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
16044      */
16045     itemCls : "x-menu-item x-menu-check-item",
16046     /**
16047      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
16048      */
16049     groupClass : "x-menu-group-item",
16050
16051     /**
16052      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
16053      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
16054      * initialized with checked = true will be rendered as checked.
16055      */
16056     checked: false,
16057
16058     // private
16059     ctype: "Roo.menu.CheckItem",
16060
16061     // private
16062     onRender : function(c){
16063         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
16064         if(this.group){
16065             this.el.addClass(this.groupClass);
16066         }
16067         Roo.menu.MenuMgr.registerCheckable(this);
16068         if(this.checked){
16069             this.checked = false;
16070             this.setChecked(true, true);
16071         }
16072     },
16073
16074     // private
16075     destroy : function(){
16076         if(this.rendered){
16077             Roo.menu.MenuMgr.unregisterCheckable(this);
16078         }
16079         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
16080     },
16081
16082     /**
16083      * Set the checked state of this item
16084      * @param {Boolean} checked The new checked value
16085      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
16086      */
16087     setChecked : function(state, suppressEvent){
16088         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
16089             if(this.container){
16090                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
16091             }
16092             this.checked = state;
16093             if(suppressEvent !== true){
16094                 this.fireEvent("checkchange", this, state);
16095             }
16096         }
16097     },
16098
16099     // private
16100     handleClick : function(e){
16101        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
16102            this.setChecked(!this.checked);
16103        }
16104        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
16105     }
16106 });/*
16107  * Based on:
16108  * Ext JS Library 1.1.1
16109  * Copyright(c) 2006-2007, Ext JS, LLC.
16110  *
16111  * Originally Released Under LGPL - original licence link has changed is not relivant.
16112  *
16113  * Fork - LGPL
16114  * <script type="text/javascript">
16115  */
16116  
16117 /**
16118  * @class Roo.menu.DateItem
16119  * @extends Roo.menu.Adapter
16120  * A menu item that wraps the {@link Roo.DatPicker} component.
16121  * @constructor
16122  * Creates a new DateItem
16123  * @param {Object} config Configuration options
16124  */
16125 Roo.menu.DateItem = function(config){
16126     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
16127     /** The Roo.DatePicker object @type Roo.DatePicker */
16128     this.picker = this.component;
16129     this.addEvents({select: true});
16130     
16131     this.picker.on("render", function(picker){
16132         picker.getEl().swallowEvent("click");
16133         picker.container.addClass("x-menu-date-item");
16134     });
16135
16136     this.picker.on("select", this.onSelect, this);
16137 };
16138
16139 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
16140     // private
16141     onSelect : function(picker, date){
16142         this.fireEvent("select", this, date, picker);
16143         Roo.menu.DateItem.superclass.handleClick.call(this);
16144     }
16145 });/*
16146  * Based on:
16147  * Ext JS Library 1.1.1
16148  * Copyright(c) 2006-2007, Ext JS, LLC.
16149  *
16150  * Originally Released Under LGPL - original licence link has changed is not relivant.
16151  *
16152  * Fork - LGPL
16153  * <script type="text/javascript">
16154  */
16155  
16156 /**
16157  * @class Roo.menu.ColorItem
16158  * @extends Roo.menu.Adapter
16159  * A menu item that wraps the {@link Roo.ColorPalette} component.
16160  * @constructor
16161  * Creates a new ColorItem
16162  * @param {Object} config Configuration options
16163  */
16164 Roo.menu.ColorItem = function(config){
16165     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
16166     /** The Roo.ColorPalette object @type Roo.ColorPalette */
16167     this.palette = this.component;
16168     this.relayEvents(this.palette, ["select"]);
16169     if(this.selectHandler){
16170         this.on('select', this.selectHandler, this.scope);
16171     }
16172 };
16173 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
16174  * Based on:
16175  * Ext JS Library 1.1.1
16176  * Copyright(c) 2006-2007, Ext JS, LLC.
16177  *
16178  * Originally Released Under LGPL - original licence link has changed is not relivant.
16179  *
16180  * Fork - LGPL
16181  * <script type="text/javascript">
16182  */
16183  
16184
16185 /**
16186  * @class Roo.menu.DateMenu
16187  * @extends Roo.menu.Menu
16188  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
16189  * @constructor
16190  * Creates a new DateMenu
16191  * @param {Object} config Configuration options
16192  */
16193 Roo.menu.DateMenu = function(config){
16194     Roo.menu.DateMenu.superclass.constructor.call(this, config);
16195     this.plain = true;
16196     var di = new Roo.menu.DateItem(config);
16197     this.add(di);
16198     /**
16199      * The {@link Roo.DatePicker} instance for this DateMenu
16200      * @type DatePicker
16201      */
16202     this.picker = di.picker;
16203     /**
16204      * @event select
16205      * @param {DatePicker} picker
16206      * @param {Date} date
16207      */
16208     this.relayEvents(di, ["select"]);
16209     this.on('beforeshow', function(){
16210         if(this.picker){
16211             this.picker.hideMonthPicker(false);
16212         }
16213     }, this);
16214 };
16215 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
16216     cls:'x-date-menu'
16217 });/*
16218  * Based on:
16219  * Ext JS Library 1.1.1
16220  * Copyright(c) 2006-2007, Ext JS, LLC.
16221  *
16222  * Originally Released Under LGPL - original licence link has changed is not relivant.
16223  *
16224  * Fork - LGPL
16225  * <script type="text/javascript">
16226  */
16227  
16228
16229 /**
16230  * @class Roo.menu.ColorMenu
16231  * @extends Roo.menu.Menu
16232  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
16233  * @constructor
16234  * Creates a new ColorMenu
16235  * @param {Object} config Configuration options
16236  */
16237 Roo.menu.ColorMenu = function(config){
16238     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
16239     this.plain = true;
16240     var ci = new Roo.menu.ColorItem(config);
16241     this.add(ci);
16242     /**
16243      * The {@link Roo.ColorPalette} instance for this ColorMenu
16244      * @type ColorPalette
16245      */
16246     this.palette = ci.palette;
16247     /**
16248      * @event select
16249      * @param {ColorPalette} palette
16250      * @param {String} color
16251      */
16252     this.relayEvents(ci, ["select"]);
16253 };
16254 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
16255  * Based on:
16256  * Ext JS Library 1.1.1
16257  * Copyright(c) 2006-2007, Ext JS, LLC.
16258  *
16259  * Originally Released Under LGPL - original licence link has changed is not relivant.
16260  *
16261  * Fork - LGPL
16262  * <script type="text/javascript">
16263  */
16264  
16265 /**
16266  * @class Roo.form.TextItem
16267  * @extends Roo.BoxComponent
16268  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
16269  * @constructor
16270  * Creates a new TextItem
16271  * @param {Object} config Configuration options
16272  */
16273 Roo.form.TextItem = function(config){
16274     Roo.form.TextItem.superclass.constructor.call(this, config);
16275 };
16276
16277 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
16278     
16279     /**
16280      * @cfg {String} tag the tag for this item (default div)
16281      */
16282     tag : 'div',
16283     /**
16284      * @cfg {String} html the content for this item
16285      */
16286     html : '',
16287     
16288     getAutoCreate : function()
16289     {
16290         var cfg = {
16291             id: this.id,
16292             tag: this.tag,
16293             html: this.html,
16294             cls: 'x-form-item'
16295         };
16296         
16297         return cfg;
16298         
16299     },
16300     
16301     onRender : function(ct, position)
16302     {
16303         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
16304         
16305         if(!this.el){
16306             var cfg = this.getAutoCreate();
16307             if(!cfg.name){
16308                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
16309             }
16310             if (!cfg.name.length) {
16311                 delete cfg.name;
16312             }
16313             this.el = ct.createChild(cfg, position);
16314         }
16315     }
16316     
16317 });/*
16318  * Based on:
16319  * Ext JS Library 1.1.1
16320  * Copyright(c) 2006-2007, Ext JS, LLC.
16321  *
16322  * Originally Released Under LGPL - original licence link has changed is not relivant.
16323  *
16324  * Fork - LGPL
16325  * <script type="text/javascript">
16326  */
16327  
16328 /**
16329  * @class Roo.form.Field
16330  * @extends Roo.BoxComponent
16331  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
16332  * @constructor
16333  * Creates a new Field
16334  * @param {Object} config Configuration options
16335  */
16336 Roo.form.Field = function(config){
16337     Roo.form.Field.superclass.constructor.call(this, config);
16338 };
16339
16340 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
16341     /**
16342      * @cfg {String} fieldLabel Label to use when rendering a form.
16343      */
16344        /**
16345      * @cfg {String} qtip Mouse over tip
16346      */
16347      
16348     /**
16349      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
16350      */
16351     invalidClass : "x-form-invalid",
16352     /**
16353      * @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")
16354      */
16355     invalidText : "The value in this field is invalid",
16356     /**
16357      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
16358      */
16359     focusClass : "x-form-focus",
16360     /**
16361      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
16362       automatic validation (defaults to "keyup").
16363      */
16364     validationEvent : "keyup",
16365     /**
16366      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
16367      */
16368     validateOnBlur : true,
16369     /**
16370      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
16371      */
16372     validationDelay : 250,
16373     /**
16374      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
16375      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
16376      */
16377     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
16378     /**
16379      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
16380      */
16381     fieldClass : "x-form-field",
16382     /**
16383      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
16384      *<pre>
16385 Value         Description
16386 -----------   ----------------------------------------------------------------------
16387 qtip          Display a quick tip when the user hovers over the field
16388 title         Display a default browser title attribute popup
16389 under         Add a block div beneath the field containing the error text
16390 side          Add an error icon to the right of the field with a popup on hover
16391 [element id]  Add the error text directly to the innerHTML of the specified element
16392 </pre>
16393      */
16394     msgTarget : 'qtip',
16395     /**
16396      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
16397      */
16398     msgFx : 'normal',
16399
16400     /**
16401      * @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.
16402      */
16403     readOnly : false,
16404
16405     /**
16406      * @cfg {Boolean} disabled True to disable the field (defaults to false).
16407      */
16408     disabled : false,
16409
16410     /**
16411      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
16412      */
16413     inputType : undefined,
16414     
16415     /**
16416      * @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).
16417          */
16418         tabIndex : undefined,
16419         
16420     // private
16421     isFormField : true,
16422
16423     // private
16424     hasFocus : false,
16425     /**
16426      * @property {Roo.Element} fieldEl
16427      * Element Containing the rendered Field (with label etc.)
16428      */
16429     /**
16430      * @cfg {Mixed} value A value to initialize this field with.
16431      */
16432     value : undefined,
16433
16434     /**
16435      * @cfg {String} name The field's HTML name attribute.
16436      */
16437     /**
16438      * @cfg {String} cls A CSS class to apply to the field's underlying element.
16439      */
16440     // private
16441     loadedValue : false,
16442      
16443      
16444         // private ??
16445         initComponent : function(){
16446         Roo.form.Field.superclass.initComponent.call(this);
16447         this.addEvents({
16448             /**
16449              * @event focus
16450              * Fires when this field receives input focus.
16451              * @param {Roo.form.Field} this
16452              */
16453             focus : true,
16454             /**
16455              * @event blur
16456              * Fires when this field loses input focus.
16457              * @param {Roo.form.Field} this
16458              */
16459             blur : true,
16460             /**
16461              * @event specialkey
16462              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
16463              * {@link Roo.EventObject#getKey} to determine which key was pressed.
16464              * @param {Roo.form.Field} this
16465              * @param {Roo.EventObject} e The event object
16466              */
16467             specialkey : true,
16468             /**
16469              * @event change
16470              * Fires just before the field blurs if the field value has changed.
16471              * @param {Roo.form.Field} this
16472              * @param {Mixed} newValue The new value
16473              * @param {Mixed} oldValue The original value
16474              */
16475             change : true,
16476             /**
16477              * @event invalid
16478              * Fires after the field has been marked as invalid.
16479              * @param {Roo.form.Field} this
16480              * @param {String} msg The validation message
16481              */
16482             invalid : true,
16483             /**
16484              * @event valid
16485              * Fires after the field has been validated with no errors.
16486              * @param {Roo.form.Field} this
16487              */
16488             valid : true,
16489              /**
16490              * @event keyup
16491              * Fires after the key up
16492              * @param {Roo.form.Field} this
16493              * @param {Roo.EventObject}  e The event Object
16494              */
16495             keyup : true
16496         });
16497     },
16498
16499     /**
16500      * Returns the name attribute of the field if available
16501      * @return {String} name The field name
16502      */
16503     getName: function(){
16504          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
16505     },
16506
16507     // private
16508     onRender : function(ct, position){
16509         Roo.form.Field.superclass.onRender.call(this, ct, position);
16510         if(!this.el){
16511             var cfg = this.getAutoCreate();
16512             if(!cfg.name){
16513                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
16514             }
16515             if (!cfg.name.length) {
16516                 delete cfg.name;
16517             }
16518             if(this.inputType){
16519                 cfg.type = this.inputType;
16520             }
16521             this.el = ct.createChild(cfg, position);
16522         }
16523         var type = this.el.dom.type;
16524         if(type){
16525             if(type == 'password'){
16526                 type = 'text';
16527             }
16528             this.el.addClass('x-form-'+type);
16529         }
16530         if(this.readOnly){
16531             this.el.dom.readOnly = true;
16532         }
16533         if(this.tabIndex !== undefined){
16534             this.el.dom.setAttribute('tabIndex', this.tabIndex);
16535         }
16536
16537         this.el.addClass([this.fieldClass, this.cls]);
16538         this.initValue();
16539     },
16540
16541     /**
16542      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
16543      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
16544      * @return {Roo.form.Field} this
16545      */
16546     applyTo : function(target){
16547         this.allowDomMove = false;
16548         this.el = Roo.get(target);
16549         this.render(this.el.dom.parentNode);
16550         return this;
16551     },
16552
16553     // private
16554     initValue : function(){
16555         if(this.value !== undefined){
16556             this.setValue(this.value);
16557         }else if(this.el.dom.value.length > 0){
16558             this.setValue(this.el.dom.value);
16559         }
16560     },
16561
16562     /**
16563      * Returns true if this field has been changed since it was originally loaded and is not disabled.
16564      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
16565      */
16566     isDirty : function() {
16567         if(this.disabled) {
16568             return false;
16569         }
16570         return String(this.getValue()) !== String(this.originalValue);
16571     },
16572
16573     /**
16574      * stores the current value in loadedValue
16575      */
16576     resetHasChanged : function()
16577     {
16578         this.loadedValue = String(this.getValue());
16579     },
16580     /**
16581      * checks the current value against the 'loaded' value.
16582      * Note - will return false if 'resetHasChanged' has not been called first.
16583      */
16584     hasChanged : function()
16585     {
16586         if(this.disabled || this.readOnly) {
16587             return false;
16588         }
16589         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
16590     },
16591     
16592     
16593     
16594     // private
16595     afterRender : function(){
16596         Roo.form.Field.superclass.afterRender.call(this);
16597         this.initEvents();
16598     },
16599
16600     // private
16601     fireKey : function(e){
16602         //Roo.log('field ' + e.getKey());
16603         if(e.isNavKeyPress()){
16604             this.fireEvent("specialkey", this, e);
16605         }
16606     },
16607
16608     /**
16609      * Resets the current field value to the originally loaded value and clears any validation messages
16610      */
16611     reset : function(){
16612         this.setValue(this.resetValue);
16613         this.originalValue = this.getValue();
16614         this.clearInvalid();
16615     },
16616
16617     // private
16618     initEvents : function(){
16619         // safari killled keypress - so keydown is now used..
16620         this.el.on("keydown" , this.fireKey,  this);
16621         this.el.on("focus", this.onFocus,  this);
16622         this.el.on("blur", this.onBlur,  this);
16623         this.el.relayEvent('keyup', this);
16624
16625         // reference to original value for reset
16626         this.originalValue = this.getValue();
16627         this.resetValue =  this.getValue();
16628     },
16629
16630     // private
16631     onFocus : function(){
16632         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16633             this.el.addClass(this.focusClass);
16634         }
16635         if(!this.hasFocus){
16636             this.hasFocus = true;
16637             this.startValue = this.getValue();
16638             this.fireEvent("focus", this);
16639         }
16640     },
16641
16642     beforeBlur : Roo.emptyFn,
16643
16644     // private
16645     onBlur : function(){
16646         this.beforeBlur();
16647         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16648             this.el.removeClass(this.focusClass);
16649         }
16650         this.hasFocus = false;
16651         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
16652             this.validate();
16653         }
16654         var v = this.getValue();
16655         if(String(v) !== String(this.startValue)){
16656             this.fireEvent('change', this, v, this.startValue);
16657         }
16658         this.fireEvent("blur", this);
16659     },
16660
16661     /**
16662      * Returns whether or not the field value is currently valid
16663      * @param {Boolean} preventMark True to disable marking the field invalid
16664      * @return {Boolean} True if the value is valid, else false
16665      */
16666     isValid : function(preventMark){
16667         if(this.disabled){
16668             return true;
16669         }
16670         var restore = this.preventMark;
16671         this.preventMark = preventMark === true;
16672         var v = this.validateValue(this.processValue(this.getRawValue()));
16673         this.preventMark = restore;
16674         return v;
16675     },
16676
16677     /**
16678      * Validates the field value
16679      * @return {Boolean} True if the value is valid, else false
16680      */
16681     validate : function(){
16682         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
16683             this.clearInvalid();
16684             return true;
16685         }
16686         return false;
16687     },
16688
16689     processValue : function(value){
16690         return value;
16691     },
16692
16693     // private
16694     // Subclasses should provide the validation implementation by overriding this
16695     validateValue : function(value){
16696         return true;
16697     },
16698
16699     /**
16700      * Mark this field as invalid
16701      * @param {String} msg The validation message
16702      */
16703     markInvalid : function(msg){
16704         if(!this.rendered || this.preventMark){ // not rendered
16705             return;
16706         }
16707         
16708         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16709         
16710         obj.el.addClass(this.invalidClass);
16711         msg = msg || this.invalidText;
16712         switch(this.msgTarget){
16713             case 'qtip':
16714                 obj.el.dom.qtip = msg;
16715                 obj.el.dom.qclass = 'x-form-invalid-tip';
16716                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
16717                     Roo.QuickTips.enable();
16718                 }
16719                 break;
16720             case 'title':
16721                 this.el.dom.title = msg;
16722                 break;
16723             case 'under':
16724                 if(!this.errorEl){
16725                     var elp = this.el.findParent('.x-form-element', 5, true);
16726                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
16727                     this.errorEl.setWidth(elp.getWidth(true)-20);
16728                 }
16729                 this.errorEl.update(msg);
16730                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
16731                 break;
16732             case 'side':
16733                 if(!this.errorIcon){
16734                     var elp = this.el.findParent('.x-form-element', 5, true);
16735                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
16736                 }
16737                 this.alignErrorIcon();
16738                 this.errorIcon.dom.qtip = msg;
16739                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
16740                 this.errorIcon.show();
16741                 this.on('resize', this.alignErrorIcon, this);
16742                 break;
16743             default:
16744                 var t = Roo.getDom(this.msgTarget);
16745                 t.innerHTML = msg;
16746                 t.style.display = this.msgDisplay;
16747                 break;
16748         }
16749         this.fireEvent('invalid', this, msg);
16750     },
16751
16752     // private
16753     alignErrorIcon : function(){
16754         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
16755     },
16756
16757     /**
16758      * Clear any invalid styles/messages for this field
16759      */
16760     clearInvalid : function(){
16761         if(!this.rendered || this.preventMark){ // not rendered
16762             return;
16763         }
16764         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16765         
16766         obj.el.removeClass(this.invalidClass);
16767         switch(this.msgTarget){
16768             case 'qtip':
16769                 obj.el.dom.qtip = '';
16770                 break;
16771             case 'title':
16772                 this.el.dom.title = '';
16773                 break;
16774             case 'under':
16775                 if(this.errorEl){
16776                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
16777                 }
16778                 break;
16779             case 'side':
16780                 if(this.errorIcon){
16781                     this.errorIcon.dom.qtip = '';
16782                     this.errorIcon.hide();
16783                     this.un('resize', this.alignErrorIcon, this);
16784                 }
16785                 break;
16786             default:
16787                 var t = Roo.getDom(this.msgTarget);
16788                 t.innerHTML = '';
16789                 t.style.display = 'none';
16790                 break;
16791         }
16792         this.fireEvent('valid', this);
16793     },
16794
16795     /**
16796      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
16797      * @return {Mixed} value The field value
16798      */
16799     getRawValue : function(){
16800         var v = this.el.getValue();
16801         
16802         return v;
16803     },
16804
16805     /**
16806      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16807      * @return {Mixed} value The field value
16808      */
16809     getValue : function(){
16810         var v = this.el.getValue();
16811          
16812         return v;
16813     },
16814
16815     /**
16816      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
16817      * @param {Mixed} value The value to set
16818      */
16819     setRawValue : function(v){
16820         return this.el.dom.value = (v === null || v === undefined ? '' : v);
16821     },
16822
16823     /**
16824      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
16825      * @param {Mixed} value The value to set
16826      */
16827     setValue : function(v){
16828         this.value = v;
16829         if(this.rendered){
16830             this.el.dom.value = (v === null || v === undefined ? '' : v);
16831              this.validate();
16832         }
16833     },
16834
16835     adjustSize : function(w, h){
16836         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
16837         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
16838         return s;
16839     },
16840
16841     adjustWidth : function(tag, w){
16842         tag = tag.toLowerCase();
16843         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
16844             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
16845                 if(tag == 'input'){
16846                     return w + 2;
16847                 }
16848                 if(tag == 'textarea'){
16849                     return w-2;
16850                 }
16851             }else if(Roo.isOpera){
16852                 if(tag == 'input'){
16853                     return w + 2;
16854                 }
16855                 if(tag == 'textarea'){
16856                     return w-2;
16857                 }
16858             }
16859         }
16860         return w;
16861     }
16862 });
16863
16864
16865 // anything other than normal should be considered experimental
16866 Roo.form.Field.msgFx = {
16867     normal : {
16868         show: function(msgEl, f){
16869             msgEl.setDisplayed('block');
16870         },
16871
16872         hide : function(msgEl, f){
16873             msgEl.setDisplayed(false).update('');
16874         }
16875     },
16876
16877     slide : {
16878         show: function(msgEl, f){
16879             msgEl.slideIn('t', {stopFx:true});
16880         },
16881
16882         hide : function(msgEl, f){
16883             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
16884         }
16885     },
16886
16887     slideRight : {
16888         show: function(msgEl, f){
16889             msgEl.fixDisplay();
16890             msgEl.alignTo(f.el, 'tl-tr');
16891             msgEl.slideIn('l', {stopFx:true});
16892         },
16893
16894         hide : function(msgEl, f){
16895             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
16896         }
16897     }
16898 };/*
16899  * Based on:
16900  * Ext JS Library 1.1.1
16901  * Copyright(c) 2006-2007, Ext JS, LLC.
16902  *
16903  * Originally Released Under LGPL - original licence link has changed is not relivant.
16904  *
16905  * Fork - LGPL
16906  * <script type="text/javascript">
16907  */
16908  
16909
16910 /**
16911  * @class Roo.form.TextField
16912  * @extends Roo.form.Field
16913  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
16914  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
16915  * @constructor
16916  * Creates a new TextField
16917  * @param {Object} config Configuration options
16918  */
16919 Roo.form.TextField = function(config){
16920     Roo.form.TextField.superclass.constructor.call(this, config);
16921     this.addEvents({
16922         /**
16923          * @event autosize
16924          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
16925          * according to the default logic, but this event provides a hook for the developer to apply additional
16926          * logic at runtime to resize the field if needed.
16927              * @param {Roo.form.Field} this This text field
16928              * @param {Number} width The new field width
16929              */
16930         autosize : true
16931     });
16932 };
16933
16934 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
16935     /**
16936      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
16937      */
16938     grow : false,
16939     /**
16940      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
16941      */
16942     growMin : 30,
16943     /**
16944      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
16945      */
16946     growMax : 800,
16947     /**
16948      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
16949      */
16950     vtype : null,
16951     /**
16952      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
16953      */
16954     maskRe : null,
16955     /**
16956      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
16957      */
16958     disableKeyFilter : false,
16959     /**
16960      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
16961      */
16962     allowBlank : true,
16963     /**
16964      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
16965      */
16966     minLength : 0,
16967     /**
16968      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
16969      */
16970     maxLength : Number.MAX_VALUE,
16971     /**
16972      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
16973      */
16974     minLengthText : "The minimum length for this field is {0}",
16975     /**
16976      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
16977      */
16978     maxLengthText : "The maximum length for this field is {0}",
16979     /**
16980      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
16981      */
16982     selectOnFocus : false,
16983     /**
16984      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
16985      */
16986     blankText : "This field is required",
16987     /**
16988      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
16989      * If available, this function will be called only after the basic validators all return true, and will be passed the
16990      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
16991      */
16992     validator : null,
16993     /**
16994      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
16995      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
16996      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
16997      */
16998     regex : null,
16999     /**
17000      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
17001      */
17002     regexText : "",
17003     /**
17004      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
17005      */
17006     emptyText : null,
17007    
17008
17009     // private
17010     initEvents : function()
17011     {
17012         if (this.emptyText) {
17013             this.el.attr('placeholder', this.emptyText);
17014         }
17015         
17016         Roo.form.TextField.superclass.initEvents.call(this);
17017         if(this.validationEvent == 'keyup'){
17018             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
17019             this.el.on('keyup', this.filterValidation, this);
17020         }
17021         else if(this.validationEvent !== false){
17022             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
17023         }
17024         
17025         if(this.selectOnFocus){
17026             this.on("focus", this.preFocus, this);
17027             
17028         }
17029         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
17030             this.el.on("keypress", this.filterKeys, this);
17031         }
17032         if(this.grow){
17033             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
17034             this.el.on("click", this.autoSize,  this);
17035         }
17036         if(this.el.is('input[type=password]') && Roo.isSafari){
17037             this.el.on('keydown', this.SafariOnKeyDown, this);
17038         }
17039     },
17040
17041     processValue : function(value){
17042         if(this.stripCharsRe){
17043             var newValue = value.replace(this.stripCharsRe, '');
17044             if(newValue !== value){
17045                 this.setRawValue(newValue);
17046                 return newValue;
17047             }
17048         }
17049         return value;
17050     },
17051
17052     filterValidation : function(e){
17053         if(!e.isNavKeyPress()){
17054             this.validationTask.delay(this.validationDelay);
17055         }
17056     },
17057
17058     // private
17059     onKeyUp : function(e){
17060         if(!e.isNavKeyPress()){
17061             this.autoSize();
17062         }
17063     },
17064
17065     /**
17066      * Resets the current field value to the originally-loaded value and clears any validation messages.
17067      *  
17068      */
17069     reset : function(){
17070         Roo.form.TextField.superclass.reset.call(this);
17071        
17072     },
17073
17074     
17075     // private
17076     preFocus : function(){
17077         
17078         if(this.selectOnFocus){
17079             this.el.dom.select();
17080         }
17081     },
17082
17083     
17084     // private
17085     filterKeys : function(e){
17086         var k = e.getKey();
17087         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
17088             return;
17089         }
17090         var c = e.getCharCode(), cc = String.fromCharCode(c);
17091         if(Roo.isIE && (e.isSpecialKey() || !cc)){
17092             return;
17093         }
17094         if(!this.maskRe.test(cc)){
17095             e.stopEvent();
17096         }
17097     },
17098
17099     setValue : function(v){
17100         
17101         Roo.form.TextField.superclass.setValue.apply(this, arguments);
17102         
17103         this.autoSize();
17104     },
17105
17106     /**
17107      * Validates a value according to the field's validation rules and marks the field as invalid
17108      * if the validation fails
17109      * @param {Mixed} value The value to validate
17110      * @return {Boolean} True if the value is valid, else false
17111      */
17112     validateValue : function(value){
17113         if(value.length < 1)  { // if it's blank
17114              if(this.allowBlank){
17115                 this.clearInvalid();
17116                 return true;
17117              }else{
17118                 this.markInvalid(this.blankText);
17119                 return false;
17120              }
17121         }
17122         if(value.length < this.minLength){
17123             this.markInvalid(String.format(this.minLengthText, this.minLength));
17124             return false;
17125         }
17126         if(value.length > this.maxLength){
17127             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
17128             return false;
17129         }
17130         if(this.vtype){
17131             var vt = Roo.form.VTypes;
17132             if(!vt[this.vtype](value, this)){
17133                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
17134                 return false;
17135             }
17136         }
17137         if(typeof this.validator == "function"){
17138             var msg = this.validator(value);
17139             if(msg !== true){
17140                 this.markInvalid(msg);
17141                 return false;
17142             }
17143         }
17144         if(this.regex && !this.regex.test(value)){
17145             this.markInvalid(this.regexText);
17146             return false;
17147         }
17148         return true;
17149     },
17150
17151     /**
17152      * Selects text in this field
17153      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
17154      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
17155      */
17156     selectText : function(start, end){
17157         var v = this.getRawValue();
17158         if(v.length > 0){
17159             start = start === undefined ? 0 : start;
17160             end = end === undefined ? v.length : end;
17161             var d = this.el.dom;
17162             if(d.setSelectionRange){
17163                 d.setSelectionRange(start, end);
17164             }else if(d.createTextRange){
17165                 var range = d.createTextRange();
17166                 range.moveStart("character", start);
17167                 range.moveEnd("character", v.length-end);
17168                 range.select();
17169             }
17170         }
17171     },
17172
17173     /**
17174      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
17175      * This only takes effect if grow = true, and fires the autosize event.
17176      */
17177     autoSize : function(){
17178         if(!this.grow || !this.rendered){
17179             return;
17180         }
17181         if(!this.metrics){
17182             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
17183         }
17184         var el = this.el;
17185         var v = el.dom.value;
17186         var d = document.createElement('div');
17187         d.appendChild(document.createTextNode(v));
17188         v = d.innerHTML;
17189         d = null;
17190         v += "&#160;";
17191         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
17192         this.el.setWidth(w);
17193         this.fireEvent("autosize", this, w);
17194     },
17195     
17196     // private
17197     SafariOnKeyDown : function(event)
17198     {
17199         // this is a workaround for a password hang bug on chrome/ webkit.
17200         
17201         var isSelectAll = false;
17202         
17203         if(this.el.dom.selectionEnd > 0){
17204             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
17205         }
17206         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
17207             event.preventDefault();
17208             this.setValue('');
17209             return;
17210         }
17211         
17212         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
17213             
17214             event.preventDefault();
17215             // this is very hacky as keydown always get's upper case.
17216             
17217             var cc = String.fromCharCode(event.getCharCode());
17218             
17219             
17220             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
17221             
17222         }
17223         
17224         
17225     }
17226 });/*
17227  * Based on:
17228  * Ext JS Library 1.1.1
17229  * Copyright(c) 2006-2007, Ext JS, LLC.
17230  *
17231  * Originally Released Under LGPL - original licence link has changed is not relivant.
17232  *
17233  * Fork - LGPL
17234  * <script type="text/javascript">
17235  */
17236  
17237 /**
17238  * @class Roo.form.Hidden
17239  * @extends Roo.form.TextField
17240  * Simple Hidden element used on forms 
17241  * 
17242  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
17243  * 
17244  * @constructor
17245  * Creates a new Hidden form element.
17246  * @param {Object} config Configuration options
17247  */
17248
17249
17250
17251 // easy hidden field...
17252 Roo.form.Hidden = function(config){
17253     Roo.form.Hidden.superclass.constructor.call(this, config);
17254 };
17255   
17256 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
17257     fieldLabel:      '',
17258     inputType:      'hidden',
17259     width:          50,
17260     allowBlank:     true,
17261     labelSeparator: '',
17262     hidden:         true,
17263     itemCls :       'x-form-item-display-none'
17264
17265
17266 });
17267
17268
17269 /*
17270  * Based on:
17271  * Ext JS Library 1.1.1
17272  * Copyright(c) 2006-2007, Ext JS, LLC.
17273  *
17274  * Originally Released Under LGPL - original licence link has changed is not relivant.
17275  *
17276  * Fork - LGPL
17277  * <script type="text/javascript">
17278  */
17279  
17280 /**
17281  * @class Roo.form.TriggerField
17282  * @extends Roo.form.TextField
17283  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
17284  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
17285  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
17286  * for which you can provide a custom implementation.  For example:
17287  * <pre><code>
17288 var trigger = new Roo.form.TriggerField();
17289 trigger.onTriggerClick = myTriggerFn;
17290 trigger.applyTo('my-field');
17291 </code></pre>
17292  *
17293  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
17294  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
17295  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
17296  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
17297  * @constructor
17298  * Create a new TriggerField.
17299  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
17300  * to the base TextField)
17301  */
17302 Roo.form.TriggerField = function(config){
17303     this.mimicing = false;
17304     Roo.form.TriggerField.superclass.constructor.call(this, config);
17305 };
17306
17307 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
17308     /**
17309      * @cfg {String} triggerClass A CSS class to apply to the trigger
17310      */
17311     /**
17312      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17313      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
17314      */
17315     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
17316     /**
17317      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
17318      */
17319     hideTrigger:false,
17320
17321     /** @cfg {Boolean} grow @hide */
17322     /** @cfg {Number} growMin @hide */
17323     /** @cfg {Number} growMax @hide */
17324
17325     /**
17326      * @hide 
17327      * @method
17328      */
17329     autoSize: Roo.emptyFn,
17330     // private
17331     monitorTab : true,
17332     // private
17333     deferHeight : true,
17334
17335     
17336     actionMode : 'wrap',
17337     // private
17338     onResize : function(w, h){
17339         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
17340         if(typeof w == 'number'){
17341             var x = w - this.trigger.getWidth();
17342             this.el.setWidth(this.adjustWidth('input', x));
17343             this.trigger.setStyle('left', x+'px');
17344         }
17345     },
17346
17347     // private
17348     adjustSize : Roo.BoxComponent.prototype.adjustSize,
17349
17350     // private
17351     getResizeEl : function(){
17352         return this.wrap;
17353     },
17354
17355     // private
17356     getPositionEl : function(){
17357         return this.wrap;
17358     },
17359
17360     // private
17361     alignErrorIcon : function(){
17362         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
17363     },
17364
17365     // private
17366     onRender : function(ct, position){
17367         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
17368         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
17369         this.trigger = this.wrap.createChild(this.triggerConfig ||
17370                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
17371         if(this.hideTrigger){
17372             this.trigger.setDisplayed(false);
17373         }
17374         this.initTrigger();
17375         if(!this.width){
17376             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
17377         }
17378     },
17379
17380     // private
17381     initTrigger : function(){
17382         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
17383         this.trigger.addClassOnOver('x-form-trigger-over');
17384         this.trigger.addClassOnClick('x-form-trigger-click');
17385     },
17386
17387     // private
17388     onDestroy : function(){
17389         if(this.trigger){
17390             this.trigger.removeAllListeners();
17391             this.trigger.remove();
17392         }
17393         if(this.wrap){
17394             this.wrap.remove();
17395         }
17396         Roo.form.TriggerField.superclass.onDestroy.call(this);
17397     },
17398
17399     // private
17400     onFocus : function(){
17401         Roo.form.TriggerField.superclass.onFocus.call(this);
17402         if(!this.mimicing){
17403             this.wrap.addClass('x-trigger-wrap-focus');
17404             this.mimicing = true;
17405             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
17406             if(this.monitorTab){
17407                 this.el.on("keydown", this.checkTab, this);
17408             }
17409         }
17410     },
17411
17412     // private
17413     checkTab : function(e){
17414         if(e.getKey() == e.TAB){
17415             this.triggerBlur();
17416         }
17417     },
17418
17419     // private
17420     onBlur : function(){
17421         // do nothing
17422     },
17423
17424     // private
17425     mimicBlur : function(e, t){
17426         if(!this.wrap.contains(t) && this.validateBlur()){
17427             this.triggerBlur();
17428         }
17429     },
17430
17431     // private
17432     triggerBlur : function(){
17433         this.mimicing = false;
17434         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
17435         if(this.monitorTab){
17436             this.el.un("keydown", this.checkTab, this);
17437         }
17438         this.wrap.removeClass('x-trigger-wrap-focus');
17439         Roo.form.TriggerField.superclass.onBlur.call(this);
17440     },
17441
17442     // private
17443     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
17444     validateBlur : function(e, t){
17445         return true;
17446     },
17447
17448     // private
17449     onDisable : function(){
17450         Roo.form.TriggerField.superclass.onDisable.call(this);
17451         if(this.wrap){
17452             this.wrap.addClass('x-item-disabled');
17453         }
17454     },
17455
17456     // private
17457     onEnable : function(){
17458         Roo.form.TriggerField.superclass.onEnable.call(this);
17459         if(this.wrap){
17460             this.wrap.removeClass('x-item-disabled');
17461         }
17462     },
17463
17464     // private
17465     onShow : function(){
17466         var ae = this.getActionEl();
17467         
17468         if(ae){
17469             ae.dom.style.display = '';
17470             ae.dom.style.visibility = 'visible';
17471         }
17472     },
17473
17474     // private
17475     
17476     onHide : function(){
17477         var ae = this.getActionEl();
17478         ae.dom.style.display = 'none';
17479     },
17480
17481     /**
17482      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
17483      * by an implementing function.
17484      * @method
17485      * @param {EventObject} e
17486      */
17487     onTriggerClick : Roo.emptyFn
17488 });
17489
17490 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
17491 // to be extended by an implementing class.  For an example of implementing this class, see the custom
17492 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
17493 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
17494     initComponent : function(){
17495         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
17496
17497         this.triggerConfig = {
17498             tag:'span', cls:'x-form-twin-triggers', cn:[
17499             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
17500             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
17501         ]};
17502     },
17503
17504     getTrigger : function(index){
17505         return this.triggers[index];
17506     },
17507
17508     initTrigger : function(){
17509         var ts = this.trigger.select('.x-form-trigger', true);
17510         this.wrap.setStyle('overflow', 'hidden');
17511         var triggerField = this;
17512         ts.each(function(t, all, index){
17513             t.hide = function(){
17514                 var w = triggerField.wrap.getWidth();
17515                 this.dom.style.display = 'none';
17516                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17517             };
17518             t.show = function(){
17519                 var w = triggerField.wrap.getWidth();
17520                 this.dom.style.display = '';
17521                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17522             };
17523             var triggerIndex = 'Trigger'+(index+1);
17524
17525             if(this['hide'+triggerIndex]){
17526                 t.dom.style.display = 'none';
17527             }
17528             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
17529             t.addClassOnOver('x-form-trigger-over');
17530             t.addClassOnClick('x-form-trigger-click');
17531         }, this);
17532         this.triggers = ts.elements;
17533     },
17534
17535     onTrigger1Click : Roo.emptyFn,
17536     onTrigger2Click : Roo.emptyFn
17537 });/*
17538  * Based on:
17539  * Ext JS Library 1.1.1
17540  * Copyright(c) 2006-2007, Ext JS, LLC.
17541  *
17542  * Originally Released Under LGPL - original licence link has changed is not relivant.
17543  *
17544  * Fork - LGPL
17545  * <script type="text/javascript">
17546  */
17547  
17548 /**
17549  * @class Roo.form.TextArea
17550  * @extends Roo.form.TextField
17551  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
17552  * support for auto-sizing.
17553  * @constructor
17554  * Creates a new TextArea
17555  * @param {Object} config Configuration options
17556  */
17557 Roo.form.TextArea = function(config){
17558     Roo.form.TextArea.superclass.constructor.call(this, config);
17559     // these are provided exchanges for backwards compat
17560     // minHeight/maxHeight were replaced by growMin/growMax to be
17561     // compatible with TextField growing config values
17562     if(this.minHeight !== undefined){
17563         this.growMin = this.minHeight;
17564     }
17565     if(this.maxHeight !== undefined){
17566         this.growMax = this.maxHeight;
17567     }
17568 };
17569
17570 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
17571     /**
17572      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
17573      */
17574     growMin : 60,
17575     /**
17576      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
17577      */
17578     growMax: 1000,
17579     /**
17580      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
17581      * in the field (equivalent to setting overflow: hidden, defaults to false)
17582      */
17583     preventScrollbars: false,
17584     /**
17585      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17586      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
17587      */
17588
17589     // private
17590     onRender : function(ct, position){
17591         if(!this.el){
17592             this.defaultAutoCreate = {
17593                 tag: "textarea",
17594                 style:"width:300px;height:60px;",
17595                 autocomplete: "new-password"
17596             };
17597         }
17598         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
17599         if(this.grow){
17600             this.textSizeEl = Roo.DomHelper.append(document.body, {
17601                 tag: "pre", cls: "x-form-grow-sizer"
17602             });
17603             if(this.preventScrollbars){
17604                 this.el.setStyle("overflow", "hidden");
17605             }
17606             this.el.setHeight(this.growMin);
17607         }
17608     },
17609
17610     onDestroy : function(){
17611         if(this.textSizeEl){
17612             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
17613         }
17614         Roo.form.TextArea.superclass.onDestroy.call(this);
17615     },
17616
17617     // private
17618     onKeyUp : function(e){
17619         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
17620             this.autoSize();
17621         }
17622     },
17623
17624     /**
17625      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
17626      * This only takes effect if grow = true, and fires the autosize event if the height changes.
17627      */
17628     autoSize : function(){
17629         if(!this.grow || !this.textSizeEl){
17630             return;
17631         }
17632         var el = this.el;
17633         var v = el.dom.value;
17634         var ts = this.textSizeEl;
17635
17636         ts.innerHTML = '';
17637         ts.appendChild(document.createTextNode(v));
17638         v = ts.innerHTML;
17639
17640         Roo.fly(ts).setWidth(this.el.getWidth());
17641         if(v.length < 1){
17642             v = "&#160;&#160;";
17643         }else{
17644             if(Roo.isIE){
17645                 v = v.replace(/\n/g, '<p>&#160;</p>');
17646             }
17647             v += "&#160;\n&#160;";
17648         }
17649         ts.innerHTML = v;
17650         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
17651         if(h != this.lastHeight){
17652             this.lastHeight = h;
17653             this.el.setHeight(h);
17654             this.fireEvent("autosize", this, h);
17655         }
17656     }
17657 });/*
17658  * Based on:
17659  * Ext JS Library 1.1.1
17660  * Copyright(c) 2006-2007, Ext JS, LLC.
17661  *
17662  * Originally Released Under LGPL - original licence link has changed is not relivant.
17663  *
17664  * Fork - LGPL
17665  * <script type="text/javascript">
17666  */
17667  
17668
17669 /**
17670  * @class Roo.form.NumberField
17671  * @extends Roo.form.TextField
17672  * Numeric text field that provides automatic keystroke filtering and numeric validation.
17673  * @constructor
17674  * Creates a new NumberField
17675  * @param {Object} config Configuration options
17676  */
17677 Roo.form.NumberField = function(config){
17678     Roo.form.NumberField.superclass.constructor.call(this, config);
17679 };
17680
17681 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
17682     /**
17683      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
17684      */
17685     fieldClass: "x-form-field x-form-num-field",
17686     /**
17687      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
17688      */
17689     allowDecimals : true,
17690     /**
17691      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
17692      */
17693     decimalSeparator : ".",
17694     /**
17695      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
17696      */
17697     decimalPrecision : 2,
17698     /**
17699      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
17700      */
17701     allowNegative : true,
17702     /**
17703      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
17704      */
17705     minValue : Number.NEGATIVE_INFINITY,
17706     /**
17707      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
17708      */
17709     maxValue : Number.MAX_VALUE,
17710     /**
17711      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
17712      */
17713     minText : "The minimum value for this field is {0}",
17714     /**
17715      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
17716      */
17717     maxText : "The maximum value for this field is {0}",
17718     /**
17719      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
17720      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
17721      */
17722     nanText : "{0} is not a valid number",
17723
17724     // private
17725     initEvents : function(){
17726         Roo.form.NumberField.superclass.initEvents.call(this);
17727         var allowed = "0123456789";
17728         if(this.allowDecimals){
17729             allowed += this.decimalSeparator;
17730         }
17731         if(this.allowNegative){
17732             allowed += "-";
17733         }
17734         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
17735         var keyPress = function(e){
17736             var k = e.getKey();
17737             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
17738                 return;
17739             }
17740             var c = e.getCharCode();
17741             if(allowed.indexOf(String.fromCharCode(c)) === -1){
17742                 e.stopEvent();
17743             }
17744         };
17745         this.el.on("keypress", keyPress, this);
17746     },
17747
17748     // private
17749     validateValue : function(value){
17750         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
17751             return false;
17752         }
17753         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17754              return true;
17755         }
17756         var num = this.parseValue(value);
17757         if(isNaN(num)){
17758             this.markInvalid(String.format(this.nanText, value));
17759             return false;
17760         }
17761         if(num < this.minValue){
17762             this.markInvalid(String.format(this.minText, this.minValue));
17763             return false;
17764         }
17765         if(num > this.maxValue){
17766             this.markInvalid(String.format(this.maxText, this.maxValue));
17767             return false;
17768         }
17769         return true;
17770     },
17771
17772     getValue : function(){
17773         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
17774     },
17775
17776     // private
17777     parseValue : function(value){
17778         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
17779         return isNaN(value) ? '' : value;
17780     },
17781
17782     // private
17783     fixPrecision : function(value){
17784         var nan = isNaN(value);
17785         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
17786             return nan ? '' : value;
17787         }
17788         return parseFloat(value).toFixed(this.decimalPrecision);
17789     },
17790
17791     setValue : function(v){
17792         v = this.fixPrecision(v);
17793         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
17794     },
17795
17796     // private
17797     decimalPrecisionFcn : function(v){
17798         return Math.floor(v);
17799     },
17800
17801     beforeBlur : function(){
17802         var v = this.parseValue(this.getRawValue());
17803         if(v){
17804             this.setValue(v);
17805         }
17806     }
17807 });/*
17808  * Based on:
17809  * Ext JS Library 1.1.1
17810  * Copyright(c) 2006-2007, Ext JS, LLC.
17811  *
17812  * Originally Released Under LGPL - original licence link has changed is not relivant.
17813  *
17814  * Fork - LGPL
17815  * <script type="text/javascript">
17816  */
17817  
17818 /**
17819  * @class Roo.form.DateField
17820  * @extends Roo.form.TriggerField
17821  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17822 * @constructor
17823 * Create a new DateField
17824 * @param {Object} config
17825  */
17826 Roo.form.DateField = function(config){
17827     Roo.form.DateField.superclass.constructor.call(this, config);
17828     
17829       this.addEvents({
17830          
17831         /**
17832          * @event select
17833          * Fires when a date is selected
17834              * @param {Roo.form.DateField} combo This combo box
17835              * @param {Date} date The date selected
17836              */
17837         'select' : true
17838          
17839     });
17840     
17841     
17842     if(typeof this.minValue == "string") {
17843         this.minValue = this.parseDate(this.minValue);
17844     }
17845     if(typeof this.maxValue == "string") {
17846         this.maxValue = this.parseDate(this.maxValue);
17847     }
17848     this.ddMatch = null;
17849     if(this.disabledDates){
17850         var dd = this.disabledDates;
17851         var re = "(?:";
17852         for(var i = 0; i < dd.length; i++){
17853             re += dd[i];
17854             if(i != dd.length-1) {
17855                 re += "|";
17856             }
17857         }
17858         this.ddMatch = new RegExp(re + ")");
17859     }
17860 };
17861
17862 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
17863     /**
17864      * @cfg {String} format
17865      * The default date format string which can be overriden for localization support.  The format must be
17866      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17867      */
17868     format : "m/d/y",
17869     /**
17870      * @cfg {String} altFormats
17871      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17872      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17873      */
17874     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17875     /**
17876      * @cfg {Array} disabledDays
17877      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17878      */
17879     disabledDays : null,
17880     /**
17881      * @cfg {String} disabledDaysText
17882      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17883      */
17884     disabledDaysText : "Disabled",
17885     /**
17886      * @cfg {Array} disabledDates
17887      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17888      * expression so they are very powerful. Some examples:
17889      * <ul>
17890      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17891      * <li>["03/08", "09/16"] would disable those days for every year</li>
17892      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17893      * <li>["03/../2006"] would disable every day in March 2006</li>
17894      * <li>["^03"] would disable every day in every March</li>
17895      * </ul>
17896      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17897      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17898      */
17899     disabledDates : null,
17900     /**
17901      * @cfg {String} disabledDatesText
17902      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17903      */
17904     disabledDatesText : "Disabled",
17905     /**
17906      * @cfg {Date/String} minValue
17907      * The minimum allowed date. Can be either a Javascript date object or a string date in a
17908      * valid format (defaults to null).
17909      */
17910     minValue : null,
17911     /**
17912      * @cfg {Date/String} maxValue
17913      * The maximum allowed date. Can be either a Javascript date object or a string date in a
17914      * valid format (defaults to null).
17915      */
17916     maxValue : null,
17917     /**
17918      * @cfg {String} minText
17919      * The error text to display when the date in the cell is before minValue (defaults to
17920      * 'The date in this field must be after {minValue}').
17921      */
17922     minText : "The date in this field must be equal to or after {0}",
17923     /**
17924      * @cfg {String} maxText
17925      * The error text to display when the date in the cell is after maxValue (defaults to
17926      * 'The date in this field must be before {maxValue}').
17927      */
17928     maxText : "The date in this field must be equal to or before {0}",
17929     /**
17930      * @cfg {String} invalidText
17931      * The error text to display when the date in the field is invalid (defaults to
17932      * '{value} is not a valid date - it must be in the format {format}').
17933      */
17934     invalidText : "{0} is not a valid date - it must be in the format {1}",
17935     /**
17936      * @cfg {String} triggerClass
17937      * An additional CSS class used to style the trigger button.  The trigger will always get the
17938      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
17939      * which displays a calendar icon).
17940      */
17941     triggerClass : 'x-form-date-trigger',
17942     
17943
17944     /**
17945      * @cfg {Boolean} useIso
17946      * if enabled, then the date field will use a hidden field to store the 
17947      * real value as iso formated date. default (false)
17948      */ 
17949     useIso : false,
17950     /**
17951      * @cfg {String/Object} autoCreate
17952      * A DomHelper element spec, or true for a default element spec (defaults to
17953      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
17954      */ 
17955     // private
17956     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
17957     
17958     // private
17959     hiddenField: false,
17960     
17961     onRender : function(ct, position)
17962     {
17963         Roo.form.DateField.superclass.onRender.call(this, ct, position);
17964         if (this.useIso) {
17965             //this.el.dom.removeAttribute('name'); 
17966             Roo.log("Changing name?");
17967             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
17968             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
17969                     'before', true);
17970             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
17971             // prevent input submission
17972             this.hiddenName = this.name;
17973         }
17974             
17975             
17976     },
17977     
17978     // private
17979     validateValue : function(value)
17980     {
17981         value = this.formatDate(value);
17982         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
17983             Roo.log('super failed');
17984             return false;
17985         }
17986         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17987              return true;
17988         }
17989         var svalue = value;
17990         value = this.parseDate(value);
17991         if(!value){
17992             Roo.log('parse date failed' + svalue);
17993             this.markInvalid(String.format(this.invalidText, svalue, this.format));
17994             return false;
17995         }
17996         var time = value.getTime();
17997         if(this.minValue && time < this.minValue.getTime()){
17998             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
17999             return false;
18000         }
18001         if(this.maxValue && time > this.maxValue.getTime()){
18002             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
18003             return false;
18004         }
18005         if(this.disabledDays){
18006             var day = value.getDay();
18007             for(var i = 0; i < this.disabledDays.length; i++) {
18008                 if(day === this.disabledDays[i]){
18009                     this.markInvalid(this.disabledDaysText);
18010                     return false;
18011                 }
18012             }
18013         }
18014         var fvalue = this.formatDate(value);
18015         if(this.ddMatch && this.ddMatch.test(fvalue)){
18016             this.markInvalid(String.format(this.disabledDatesText, fvalue));
18017             return false;
18018         }
18019         return true;
18020     },
18021
18022     // private
18023     // Provides logic to override the default TriggerField.validateBlur which just returns true
18024     validateBlur : function(){
18025         return !this.menu || !this.menu.isVisible();
18026     },
18027     
18028     getName: function()
18029     {
18030         // returns hidden if it's set..
18031         if (!this.rendered) {return ''};
18032         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
18033         
18034     },
18035
18036     /**
18037      * Returns the current date value of the date field.
18038      * @return {Date} The date value
18039      */
18040     getValue : function(){
18041         
18042         return  this.hiddenField ?
18043                 this.hiddenField.value :
18044                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
18045     },
18046
18047     /**
18048      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
18049      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
18050      * (the default format used is "m/d/y").
18051      * <br />Usage:
18052      * <pre><code>
18053 //All of these calls set the same date value (May 4, 2006)
18054
18055 //Pass a date object:
18056 var dt = new Date('5/4/06');
18057 dateField.setValue(dt);
18058
18059 //Pass a date string (default format):
18060 dateField.setValue('5/4/06');
18061
18062 //Pass a date string (custom format):
18063 dateField.format = 'Y-m-d';
18064 dateField.setValue('2006-5-4');
18065 </code></pre>
18066      * @param {String/Date} date The date or valid date string
18067      */
18068     setValue : function(date){
18069         if (this.hiddenField) {
18070             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
18071         }
18072         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
18073         // make sure the value field is always stored as a date..
18074         this.value = this.parseDate(date);
18075         
18076         
18077     },
18078
18079     // private
18080     parseDate : function(value){
18081         if(!value || value instanceof Date){
18082             return value;
18083         }
18084         var v = Date.parseDate(value, this.format);
18085          if (!v && this.useIso) {
18086             v = Date.parseDate(value, 'Y-m-d');
18087         }
18088         if(!v && this.altFormats){
18089             if(!this.altFormatsArray){
18090                 this.altFormatsArray = this.altFormats.split("|");
18091             }
18092             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18093                 v = Date.parseDate(value, this.altFormatsArray[i]);
18094             }
18095         }
18096         return v;
18097     },
18098
18099     // private
18100     formatDate : function(date, fmt){
18101         return (!date || !(date instanceof Date)) ?
18102                date : date.dateFormat(fmt || this.format);
18103     },
18104
18105     // private
18106     menuListeners : {
18107         select: function(m, d){
18108             
18109             this.setValue(d);
18110             this.fireEvent('select', this, d);
18111         },
18112         show : function(){ // retain focus styling
18113             this.onFocus();
18114         },
18115         hide : function(){
18116             this.focus.defer(10, this);
18117             var ml = this.menuListeners;
18118             this.menu.un("select", ml.select,  this);
18119             this.menu.un("show", ml.show,  this);
18120             this.menu.un("hide", ml.hide,  this);
18121         }
18122     },
18123
18124     // private
18125     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
18126     onTriggerClick : function(){
18127         if(this.disabled){
18128             return;
18129         }
18130         if(this.menu == null){
18131             this.menu = new Roo.menu.DateMenu();
18132         }
18133         Roo.apply(this.menu.picker,  {
18134             showClear: this.allowBlank,
18135             minDate : this.minValue,
18136             maxDate : this.maxValue,
18137             disabledDatesRE : this.ddMatch,
18138             disabledDatesText : this.disabledDatesText,
18139             disabledDays : this.disabledDays,
18140             disabledDaysText : this.disabledDaysText,
18141             format : this.useIso ? 'Y-m-d' : this.format,
18142             minText : String.format(this.minText, this.formatDate(this.minValue)),
18143             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
18144         });
18145         this.menu.on(Roo.apply({}, this.menuListeners, {
18146             scope:this
18147         }));
18148         this.menu.picker.setValue(this.getValue() || new Date());
18149         this.menu.show(this.el, "tl-bl?");
18150     },
18151
18152     beforeBlur : function(){
18153         var v = this.parseDate(this.getRawValue());
18154         if(v){
18155             this.setValue(v);
18156         }
18157     },
18158
18159     /*@
18160      * overide
18161      * 
18162      */
18163     isDirty : function() {
18164         if(this.disabled) {
18165             return false;
18166         }
18167         
18168         if(typeof(this.startValue) === 'undefined'){
18169             return false;
18170         }
18171         
18172         return String(this.getValue()) !== String(this.startValue);
18173         
18174     }
18175 });/*
18176  * Based on:
18177  * Ext JS Library 1.1.1
18178  * Copyright(c) 2006-2007, Ext JS, LLC.
18179  *
18180  * Originally Released Under LGPL - original licence link has changed is not relivant.
18181  *
18182  * Fork - LGPL
18183  * <script type="text/javascript">
18184  */
18185  
18186 /**
18187  * @class Roo.form.MonthField
18188  * @extends Roo.form.TriggerField
18189  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
18190 * @constructor
18191 * Create a new MonthField
18192 * @param {Object} config
18193  */
18194 Roo.form.MonthField = function(config){
18195     
18196     Roo.form.MonthField.superclass.constructor.call(this, config);
18197     
18198       this.addEvents({
18199          
18200         /**
18201          * @event select
18202          * Fires when a date is selected
18203              * @param {Roo.form.MonthFieeld} combo This combo box
18204              * @param {Date} date The date selected
18205              */
18206         'select' : true
18207          
18208     });
18209     
18210     
18211     if(typeof this.minValue == "string") {
18212         this.minValue = this.parseDate(this.minValue);
18213     }
18214     if(typeof this.maxValue == "string") {
18215         this.maxValue = this.parseDate(this.maxValue);
18216     }
18217     this.ddMatch = null;
18218     if(this.disabledDates){
18219         var dd = this.disabledDates;
18220         var re = "(?:";
18221         for(var i = 0; i < dd.length; i++){
18222             re += dd[i];
18223             if(i != dd.length-1) {
18224                 re += "|";
18225             }
18226         }
18227         this.ddMatch = new RegExp(re + ")");
18228     }
18229 };
18230
18231 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
18232     /**
18233      * @cfg {String} format
18234      * The default date format string which can be overriden for localization support.  The format must be
18235      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18236      */
18237     format : "M Y",
18238     /**
18239      * @cfg {String} altFormats
18240      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18241      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18242      */
18243     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
18244     /**
18245      * @cfg {Array} disabledDays
18246      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
18247      */
18248     disabledDays : [0,1,2,3,4,5,6],
18249     /**
18250      * @cfg {String} disabledDaysText
18251      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
18252      */
18253     disabledDaysText : "Disabled",
18254     /**
18255      * @cfg {Array} disabledDates
18256      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
18257      * expression so they are very powerful. Some examples:
18258      * <ul>
18259      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
18260      * <li>["03/08", "09/16"] would disable those days for every year</li>
18261      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
18262      * <li>["03/../2006"] would disable every day in March 2006</li>
18263      * <li>["^03"] would disable every day in every March</li>
18264      * </ul>
18265      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
18266      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
18267      */
18268     disabledDates : null,
18269     /**
18270      * @cfg {String} disabledDatesText
18271      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
18272      */
18273     disabledDatesText : "Disabled",
18274     /**
18275      * @cfg {Date/String} minValue
18276      * The minimum allowed date. Can be either a Javascript date object or a string date in a
18277      * valid format (defaults to null).
18278      */
18279     minValue : null,
18280     /**
18281      * @cfg {Date/String} maxValue
18282      * The maximum allowed date. Can be either a Javascript date object or a string date in a
18283      * valid format (defaults to null).
18284      */
18285     maxValue : null,
18286     /**
18287      * @cfg {String} minText
18288      * The error text to display when the date in the cell is before minValue (defaults to
18289      * 'The date in this field must be after {minValue}').
18290      */
18291     minText : "The date in this field must be equal to or after {0}",
18292     /**
18293      * @cfg {String} maxTextf
18294      * The error text to display when the date in the cell is after maxValue (defaults to
18295      * 'The date in this field must be before {maxValue}').
18296      */
18297     maxText : "The date in this field must be equal to or before {0}",
18298     /**
18299      * @cfg {String} invalidText
18300      * The error text to display when the date in the field is invalid (defaults to
18301      * '{value} is not a valid date - it must be in the format {format}').
18302      */
18303     invalidText : "{0} is not a valid date - it must be in the format {1}",
18304     /**
18305      * @cfg {String} triggerClass
18306      * An additional CSS class used to style the trigger button.  The trigger will always get the
18307      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
18308      * which displays a calendar icon).
18309      */
18310     triggerClass : 'x-form-date-trigger',
18311     
18312
18313     /**
18314      * @cfg {Boolean} useIso
18315      * if enabled, then the date field will use a hidden field to store the 
18316      * real value as iso formated date. default (true)
18317      */ 
18318     useIso : true,
18319     /**
18320      * @cfg {String/Object} autoCreate
18321      * A DomHelper element spec, or true for a default element spec (defaults to
18322      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
18323      */ 
18324     // private
18325     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
18326     
18327     // private
18328     hiddenField: false,
18329     
18330     hideMonthPicker : false,
18331     
18332     onRender : function(ct, position)
18333     {
18334         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
18335         if (this.useIso) {
18336             this.el.dom.removeAttribute('name'); 
18337             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
18338                     'before', true);
18339             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
18340             // prevent input submission
18341             this.hiddenName = this.name;
18342         }
18343             
18344             
18345     },
18346     
18347     // private
18348     validateValue : function(value)
18349     {
18350         value = this.formatDate(value);
18351         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
18352             return false;
18353         }
18354         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
18355              return true;
18356         }
18357         var svalue = value;
18358         value = this.parseDate(value);
18359         if(!value){
18360             this.markInvalid(String.format(this.invalidText, svalue, this.format));
18361             return false;
18362         }
18363         var time = value.getTime();
18364         if(this.minValue && time < this.minValue.getTime()){
18365             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
18366             return false;
18367         }
18368         if(this.maxValue && time > this.maxValue.getTime()){
18369             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
18370             return false;
18371         }
18372         /*if(this.disabledDays){
18373             var day = value.getDay();
18374             for(var i = 0; i < this.disabledDays.length; i++) {
18375                 if(day === this.disabledDays[i]){
18376                     this.markInvalid(this.disabledDaysText);
18377                     return false;
18378                 }
18379             }
18380         }
18381         */
18382         var fvalue = this.formatDate(value);
18383         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
18384             this.markInvalid(String.format(this.disabledDatesText, fvalue));
18385             return false;
18386         }
18387         */
18388         return true;
18389     },
18390
18391     // private
18392     // Provides logic to override the default TriggerField.validateBlur which just returns true
18393     validateBlur : function(){
18394         return !this.menu || !this.menu.isVisible();
18395     },
18396
18397     /**
18398      * Returns the current date value of the date field.
18399      * @return {Date} The date value
18400      */
18401     getValue : function(){
18402         
18403         
18404         
18405         return  this.hiddenField ?
18406                 this.hiddenField.value :
18407                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
18408     },
18409
18410     /**
18411      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
18412      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
18413      * (the default format used is "m/d/y").
18414      * <br />Usage:
18415      * <pre><code>
18416 //All of these calls set the same date value (May 4, 2006)
18417
18418 //Pass a date object:
18419 var dt = new Date('5/4/06');
18420 monthField.setValue(dt);
18421
18422 //Pass a date string (default format):
18423 monthField.setValue('5/4/06');
18424
18425 //Pass a date string (custom format):
18426 monthField.format = 'Y-m-d';
18427 monthField.setValue('2006-5-4');
18428 </code></pre>
18429      * @param {String/Date} date The date or valid date string
18430      */
18431     setValue : function(date){
18432         Roo.log('month setValue' + date);
18433         // can only be first of month..
18434         
18435         var val = this.parseDate(date);
18436         
18437         if (this.hiddenField) {
18438             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
18439         }
18440         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
18441         this.value = this.parseDate(date);
18442     },
18443
18444     // private
18445     parseDate : function(value){
18446         if(!value || value instanceof Date){
18447             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
18448             return value;
18449         }
18450         var v = Date.parseDate(value, this.format);
18451         if (!v && this.useIso) {
18452             v = Date.parseDate(value, 'Y-m-d');
18453         }
18454         if (v) {
18455             // 
18456             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
18457         }
18458         
18459         
18460         if(!v && this.altFormats){
18461             if(!this.altFormatsArray){
18462                 this.altFormatsArray = this.altFormats.split("|");
18463             }
18464             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18465                 v = Date.parseDate(value, this.altFormatsArray[i]);
18466             }
18467         }
18468         return v;
18469     },
18470
18471     // private
18472     formatDate : function(date, fmt){
18473         return (!date || !(date instanceof Date)) ?
18474                date : date.dateFormat(fmt || this.format);
18475     },
18476
18477     // private
18478     menuListeners : {
18479         select: function(m, d){
18480             this.setValue(d);
18481             this.fireEvent('select', this, d);
18482         },
18483         show : function(){ // retain focus styling
18484             this.onFocus();
18485         },
18486         hide : function(){
18487             this.focus.defer(10, this);
18488             var ml = this.menuListeners;
18489             this.menu.un("select", ml.select,  this);
18490             this.menu.un("show", ml.show,  this);
18491             this.menu.un("hide", ml.hide,  this);
18492         }
18493     },
18494     // private
18495     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
18496     onTriggerClick : function(){
18497         if(this.disabled){
18498             return;
18499         }
18500         if(this.menu == null){
18501             this.menu = new Roo.menu.DateMenu();
18502            
18503         }
18504         
18505         Roo.apply(this.menu.picker,  {
18506             
18507             showClear: this.allowBlank,
18508             minDate : this.minValue,
18509             maxDate : this.maxValue,
18510             disabledDatesRE : this.ddMatch,
18511             disabledDatesText : this.disabledDatesText,
18512             
18513             format : this.useIso ? 'Y-m-d' : this.format,
18514             minText : String.format(this.minText, this.formatDate(this.minValue)),
18515             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
18516             
18517         });
18518          this.menu.on(Roo.apply({}, this.menuListeners, {
18519             scope:this
18520         }));
18521        
18522         
18523         var m = this.menu;
18524         var p = m.picker;
18525         
18526         // hide month picker get's called when we called by 'before hide';
18527         
18528         var ignorehide = true;
18529         p.hideMonthPicker  = function(disableAnim){
18530             if (ignorehide) {
18531                 return;
18532             }
18533              if(this.monthPicker){
18534                 Roo.log("hideMonthPicker called");
18535                 if(disableAnim === true){
18536                     this.monthPicker.hide();
18537                 }else{
18538                     this.monthPicker.slideOut('t', {duration:.2});
18539                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
18540                     p.fireEvent("select", this, this.value);
18541                     m.hide();
18542                 }
18543             }
18544         }
18545         
18546         Roo.log('picker set value');
18547         Roo.log(this.getValue());
18548         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
18549         m.show(this.el, 'tl-bl?');
18550         ignorehide  = false;
18551         // this will trigger hideMonthPicker..
18552         
18553         
18554         // hidden the day picker
18555         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
18556         
18557         
18558         
18559       
18560         
18561         p.showMonthPicker.defer(100, p);
18562     
18563         
18564        
18565     },
18566
18567     beforeBlur : function(){
18568         var v = this.parseDate(this.getRawValue());
18569         if(v){
18570             this.setValue(v);
18571         }
18572     }
18573
18574     /** @cfg {Boolean} grow @hide */
18575     /** @cfg {Number} growMin @hide */
18576     /** @cfg {Number} growMax @hide */
18577     /**
18578      * @hide
18579      * @method autoSize
18580      */
18581 });/*
18582  * Based on:
18583  * Ext JS Library 1.1.1
18584  * Copyright(c) 2006-2007, Ext JS, LLC.
18585  *
18586  * Originally Released Under LGPL - original licence link has changed is not relivant.
18587  *
18588  * Fork - LGPL
18589  * <script type="text/javascript">
18590  */
18591  
18592
18593 /**
18594  * @class Roo.form.ComboBox
18595  * @extends Roo.form.TriggerField
18596  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
18597  * @constructor
18598  * Create a new ComboBox.
18599  * @param {Object} config Configuration options
18600  */
18601 Roo.form.ComboBox = function(config){
18602     Roo.form.ComboBox.superclass.constructor.call(this, config);
18603     this.addEvents({
18604         /**
18605          * @event expand
18606          * Fires when the dropdown list is expanded
18607              * @param {Roo.form.ComboBox} combo This combo box
18608              */
18609         'expand' : true,
18610         /**
18611          * @event collapse
18612          * Fires when the dropdown list is collapsed
18613              * @param {Roo.form.ComboBox} combo This combo box
18614              */
18615         'collapse' : true,
18616         /**
18617          * @event beforeselect
18618          * Fires before a list item is selected. Return false to cancel the selection.
18619              * @param {Roo.form.ComboBox} combo This combo box
18620              * @param {Roo.data.Record} record The data record returned from the underlying store
18621              * @param {Number} index The index of the selected item in the dropdown list
18622              */
18623         'beforeselect' : true,
18624         /**
18625          * @event select
18626          * Fires when a list item is selected
18627              * @param {Roo.form.ComboBox} combo This combo box
18628              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
18629              * @param {Number} index The index of the selected item in the dropdown list
18630              */
18631         'select' : true,
18632         /**
18633          * @event beforequery
18634          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
18635          * The event object passed has these properties:
18636              * @param {Roo.form.ComboBox} combo This combo box
18637              * @param {String} query The query
18638              * @param {Boolean} forceAll true to force "all" query
18639              * @param {Boolean} cancel true to cancel the query
18640              * @param {Object} e The query event object
18641              */
18642         'beforequery': true,
18643          /**
18644          * @event add
18645          * Fires when the 'add' icon is pressed (add a listener to enable add button)
18646              * @param {Roo.form.ComboBox} combo This combo box
18647              */
18648         'add' : true,
18649         /**
18650          * @event edit
18651          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
18652              * @param {Roo.form.ComboBox} combo This combo box
18653              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
18654              */
18655         'edit' : true
18656         
18657         
18658     });
18659     if(this.transform){
18660         this.allowDomMove = false;
18661         var s = Roo.getDom(this.transform);
18662         if(!this.hiddenName){
18663             this.hiddenName = s.name;
18664         }
18665         if(!this.store){
18666             this.mode = 'local';
18667             var d = [], opts = s.options;
18668             for(var i = 0, len = opts.length;i < len; i++){
18669                 var o = opts[i];
18670                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
18671                 if(o.selected) {
18672                     this.value = value;
18673                 }
18674                 d.push([value, o.text]);
18675             }
18676             this.store = new Roo.data.SimpleStore({
18677                 'id': 0,
18678                 fields: ['value', 'text'],
18679                 data : d
18680             });
18681             this.valueField = 'value';
18682             this.displayField = 'text';
18683         }
18684         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
18685         if(!this.lazyRender){
18686             this.target = true;
18687             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
18688             s.parentNode.removeChild(s); // remove it
18689             this.render(this.el.parentNode);
18690         }else{
18691             s.parentNode.removeChild(s); // remove it
18692         }
18693
18694     }
18695     if (this.store) {
18696         this.store = Roo.factory(this.store, Roo.data);
18697     }
18698     
18699     this.selectedIndex = -1;
18700     if(this.mode == 'local'){
18701         if(config.queryDelay === undefined){
18702             this.queryDelay = 10;
18703         }
18704         if(config.minChars === undefined){
18705             this.minChars = 0;
18706         }
18707     }
18708 };
18709
18710 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
18711     /**
18712      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
18713      */
18714     /**
18715      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
18716      * rendering into an Roo.Editor, defaults to false)
18717      */
18718     /**
18719      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
18720      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
18721      */
18722     /**
18723      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
18724      */
18725     /**
18726      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
18727      * the dropdown list (defaults to undefined, with no header element)
18728      */
18729
18730      /**
18731      * @cfg {String/Roo.Template} tpl The template to use to render the output
18732      */
18733      
18734     // private
18735     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
18736     /**
18737      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
18738      */
18739     listWidth: undefined,
18740     /**
18741      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
18742      * mode = 'remote' or 'text' if mode = 'local')
18743      */
18744     displayField: undefined,
18745     /**
18746      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
18747      * mode = 'remote' or 'value' if mode = 'local'). 
18748      * Note: use of a valueField requires the user make a selection
18749      * in order for a value to be mapped.
18750      */
18751     valueField: undefined,
18752     
18753     
18754     /**
18755      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
18756      * field's data value (defaults to the underlying DOM element's name)
18757      */
18758     hiddenName: undefined,
18759     /**
18760      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
18761      */
18762     listClass: '',
18763     /**
18764      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
18765      */
18766     selectedClass: 'x-combo-selected',
18767     /**
18768      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
18769      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
18770      * which displays a downward arrow icon).
18771      */
18772     triggerClass : 'x-form-arrow-trigger',
18773     /**
18774      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
18775      */
18776     shadow:'sides',
18777     /**
18778      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
18779      * anchor positions (defaults to 'tl-bl')
18780      */
18781     listAlign: 'tl-bl?',
18782     /**
18783      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
18784      */
18785     maxHeight: 300,
18786     /**
18787      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
18788      * query specified by the allQuery config option (defaults to 'query')
18789      */
18790     triggerAction: 'query',
18791     /**
18792      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
18793      * (defaults to 4, does not apply if editable = false)
18794      */
18795     minChars : 4,
18796     /**
18797      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
18798      * delay (typeAheadDelay) if it matches a known value (defaults to false)
18799      */
18800     typeAhead: false,
18801     /**
18802      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
18803      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
18804      */
18805     queryDelay: 500,
18806     /**
18807      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
18808      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
18809      */
18810     pageSize: 0,
18811     /**
18812      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
18813      * when editable = true (defaults to false)
18814      */
18815     selectOnFocus:false,
18816     /**
18817      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
18818      */
18819     queryParam: 'query',
18820     /**
18821      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
18822      * when mode = 'remote' (defaults to 'Loading...')
18823      */
18824     loadingText: 'Loading...',
18825     /**
18826      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
18827      */
18828     resizable: false,
18829     /**
18830      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
18831      */
18832     handleHeight : 8,
18833     /**
18834      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
18835      * traditional select (defaults to true)
18836      */
18837     editable: true,
18838     /**
18839      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
18840      */
18841     allQuery: '',
18842     /**
18843      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
18844      */
18845     mode: 'remote',
18846     /**
18847      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
18848      * listWidth has a higher value)
18849      */
18850     minListWidth : 70,
18851     /**
18852      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
18853      * allow the user to set arbitrary text into the field (defaults to false)
18854      */
18855     forceSelection:false,
18856     /**
18857      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
18858      * if typeAhead = true (defaults to 250)
18859      */
18860     typeAheadDelay : 250,
18861     /**
18862      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
18863      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
18864      */
18865     valueNotFoundText : undefined,
18866     /**
18867      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
18868      */
18869     blockFocus : false,
18870     
18871     /**
18872      * @cfg {Boolean} disableClear Disable showing of clear button.
18873      */
18874     disableClear : false,
18875     /**
18876      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
18877      */
18878     alwaysQuery : false,
18879     
18880     //private
18881     addicon : false,
18882     editicon: false,
18883     
18884     // element that contains real text value.. (when hidden is used..)
18885      
18886     // private
18887     onRender : function(ct, position){
18888         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
18889         if(this.hiddenName){
18890             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
18891                     'before', true);
18892             this.hiddenField.value =
18893                 this.hiddenValue !== undefined ? this.hiddenValue :
18894                 this.value !== undefined ? this.value : '';
18895
18896             // prevent input submission
18897             this.el.dom.removeAttribute('name');
18898              
18899              
18900         }
18901         if(Roo.isGecko){
18902             this.el.dom.setAttribute('autocomplete', 'off');
18903         }
18904
18905         var cls = 'x-combo-list';
18906
18907         this.list = new Roo.Layer({
18908             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
18909         });
18910
18911         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
18912         this.list.setWidth(lw);
18913         this.list.swallowEvent('mousewheel');
18914         this.assetHeight = 0;
18915
18916         if(this.title){
18917             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
18918             this.assetHeight += this.header.getHeight();
18919         }
18920
18921         this.innerList = this.list.createChild({cls:cls+'-inner'});
18922         this.innerList.on('mouseover', this.onViewOver, this);
18923         this.innerList.on('mousemove', this.onViewMove, this);
18924         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18925         
18926         if(this.allowBlank && !this.pageSize && !this.disableClear){
18927             this.footer = this.list.createChild({cls:cls+'-ft'});
18928             this.pageTb = new Roo.Toolbar(this.footer);
18929            
18930         }
18931         if(this.pageSize){
18932             this.footer = this.list.createChild({cls:cls+'-ft'});
18933             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
18934                     {pageSize: this.pageSize});
18935             
18936         }
18937         
18938         if (this.pageTb && this.allowBlank && !this.disableClear) {
18939             var _this = this;
18940             this.pageTb.add(new Roo.Toolbar.Fill(), {
18941                 cls: 'x-btn-icon x-btn-clear',
18942                 text: '&#160;',
18943                 handler: function()
18944                 {
18945                     _this.collapse();
18946                     _this.clearValue();
18947                     _this.onSelect(false, -1);
18948                 }
18949             });
18950         }
18951         if (this.footer) {
18952             this.assetHeight += this.footer.getHeight();
18953         }
18954         
18955
18956         if(!this.tpl){
18957             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
18958         }
18959
18960         this.view = new Roo.View(this.innerList, this.tpl, {
18961             singleSelect:true, store: this.store, selectedClass: this.selectedClass
18962         });
18963
18964         this.view.on('click', this.onViewClick, this);
18965
18966         this.store.on('beforeload', this.onBeforeLoad, this);
18967         this.store.on('load', this.onLoad, this);
18968         this.store.on('loadexception', this.onLoadException, this);
18969
18970         if(this.resizable){
18971             this.resizer = new Roo.Resizable(this.list,  {
18972                pinned:true, handles:'se'
18973             });
18974             this.resizer.on('resize', function(r, w, h){
18975                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
18976                 this.listWidth = w;
18977                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
18978                 this.restrictHeight();
18979             }, this);
18980             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
18981         }
18982         if(!this.editable){
18983             this.editable = true;
18984             this.setEditable(false);
18985         }  
18986         
18987         
18988         if (typeof(this.events.add.listeners) != 'undefined') {
18989             
18990             this.addicon = this.wrap.createChild(
18991                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
18992        
18993             this.addicon.on('click', function(e) {
18994                 this.fireEvent('add', this);
18995             }, this);
18996         }
18997         if (typeof(this.events.edit.listeners) != 'undefined') {
18998             
18999             this.editicon = this.wrap.createChild(
19000                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
19001             if (this.addicon) {
19002                 this.editicon.setStyle('margin-left', '40px');
19003             }
19004             this.editicon.on('click', function(e) {
19005                 
19006                 // we fire even  if inothing is selected..
19007                 this.fireEvent('edit', this, this.lastData );
19008                 
19009             }, this);
19010         }
19011         
19012         
19013         
19014     },
19015
19016     // private
19017     initEvents : function(){
19018         Roo.form.ComboBox.superclass.initEvents.call(this);
19019
19020         this.keyNav = new Roo.KeyNav(this.el, {
19021             "up" : function(e){
19022                 this.inKeyMode = true;
19023                 this.selectPrev();
19024             },
19025
19026             "down" : function(e){
19027                 if(!this.isExpanded()){
19028                     this.onTriggerClick();
19029                 }else{
19030                     this.inKeyMode = true;
19031                     this.selectNext();
19032                 }
19033             },
19034
19035             "enter" : function(e){
19036                 this.onViewClick();
19037                 //return true;
19038             },
19039
19040             "esc" : function(e){
19041                 this.collapse();
19042             },
19043
19044             "tab" : function(e){
19045                 this.onViewClick(false);
19046                 this.fireEvent("specialkey", this, e);
19047                 return true;
19048             },
19049
19050             scope : this,
19051
19052             doRelay : function(foo, bar, hname){
19053                 if(hname == 'down' || this.scope.isExpanded()){
19054                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
19055                 }
19056                 return true;
19057             },
19058
19059             forceKeyDown: true
19060         });
19061         this.queryDelay = Math.max(this.queryDelay || 10,
19062                 this.mode == 'local' ? 10 : 250);
19063         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
19064         if(this.typeAhead){
19065             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
19066         }
19067         if(this.editable !== false){
19068             this.el.on("keyup", this.onKeyUp, this);
19069         }
19070         if(this.forceSelection){
19071             this.on('blur', this.doForce, this);
19072         }
19073     },
19074
19075     onDestroy : function(){
19076         if(this.view){
19077             this.view.setStore(null);
19078             this.view.el.removeAllListeners();
19079             this.view.el.remove();
19080             this.view.purgeListeners();
19081         }
19082         if(this.list){
19083             this.list.destroy();
19084         }
19085         if(this.store){
19086             this.store.un('beforeload', this.onBeforeLoad, this);
19087             this.store.un('load', this.onLoad, this);
19088             this.store.un('loadexception', this.onLoadException, this);
19089         }
19090         Roo.form.ComboBox.superclass.onDestroy.call(this);
19091     },
19092
19093     // private
19094     fireKey : function(e){
19095         if(e.isNavKeyPress() && !this.list.isVisible()){
19096             this.fireEvent("specialkey", this, e);
19097         }
19098     },
19099
19100     // private
19101     onResize: function(w, h){
19102         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
19103         
19104         if(typeof w != 'number'){
19105             // we do not handle it!?!?
19106             return;
19107         }
19108         var tw = this.trigger.getWidth();
19109         tw += this.addicon ? this.addicon.getWidth() : 0;
19110         tw += this.editicon ? this.editicon.getWidth() : 0;
19111         var x = w - tw;
19112         this.el.setWidth( this.adjustWidth('input', x));
19113             
19114         this.trigger.setStyle('left', x+'px');
19115         
19116         if(this.list && this.listWidth === undefined){
19117             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
19118             this.list.setWidth(lw);
19119             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19120         }
19121         
19122     
19123         
19124     },
19125
19126     /**
19127      * Allow or prevent the user from directly editing the field text.  If false is passed,
19128      * the user will only be able to select from the items defined in the dropdown list.  This method
19129      * is the runtime equivalent of setting the 'editable' config option at config time.
19130      * @param {Boolean} value True to allow the user to directly edit the field text
19131      */
19132     setEditable : function(value){
19133         if(value == this.editable){
19134             return;
19135         }
19136         this.editable = value;
19137         if(!value){
19138             this.el.dom.setAttribute('readOnly', true);
19139             this.el.on('mousedown', this.onTriggerClick,  this);
19140             this.el.addClass('x-combo-noedit');
19141         }else{
19142             this.el.dom.setAttribute('readOnly', false);
19143             this.el.un('mousedown', this.onTriggerClick,  this);
19144             this.el.removeClass('x-combo-noedit');
19145         }
19146     },
19147
19148     // private
19149     onBeforeLoad : function(){
19150         if(!this.hasFocus){
19151             return;
19152         }
19153         this.innerList.update(this.loadingText ?
19154                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
19155         this.restrictHeight();
19156         this.selectedIndex = -1;
19157     },
19158
19159     // private
19160     onLoad : function(){
19161         if(!this.hasFocus){
19162             return;
19163         }
19164         if(this.store.getCount() > 0){
19165             this.expand();
19166             this.restrictHeight();
19167             if(this.lastQuery == this.allQuery){
19168                 if(this.editable){
19169                     this.el.dom.select();
19170                 }
19171                 if(!this.selectByValue(this.value, true)){
19172                     this.select(0, true);
19173                 }
19174             }else{
19175                 this.selectNext();
19176                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
19177                     this.taTask.delay(this.typeAheadDelay);
19178                 }
19179             }
19180         }else{
19181             this.onEmptyResults();
19182         }
19183         //this.el.focus();
19184     },
19185     // private
19186     onLoadException : function()
19187     {
19188         this.collapse();
19189         Roo.log(this.store.reader.jsonData);
19190         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
19191             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
19192         }
19193         
19194         
19195     },
19196     // private
19197     onTypeAhead : function(){
19198         if(this.store.getCount() > 0){
19199             var r = this.store.getAt(0);
19200             var newValue = r.data[this.displayField];
19201             var len = newValue.length;
19202             var selStart = this.getRawValue().length;
19203             if(selStart != len){
19204                 this.setRawValue(newValue);
19205                 this.selectText(selStart, newValue.length);
19206             }
19207         }
19208     },
19209
19210     // private
19211     onSelect : function(record, index){
19212         if(this.fireEvent('beforeselect', this, record, index) !== false){
19213             this.setFromData(index > -1 ? record.data : false);
19214             this.collapse();
19215             this.fireEvent('select', this, record, index);
19216         }
19217     },
19218
19219     /**
19220      * Returns the currently selected field value or empty string if no value is set.
19221      * @return {String} value The selected value
19222      */
19223     getValue : function(){
19224         if(this.valueField){
19225             return typeof this.value != 'undefined' ? this.value : '';
19226         }
19227         return Roo.form.ComboBox.superclass.getValue.call(this);
19228     },
19229
19230     /**
19231      * Clears any text/value currently set in the field
19232      */
19233     clearValue : function(){
19234         if(this.hiddenField){
19235             this.hiddenField.value = '';
19236         }
19237         this.value = '';
19238         this.setRawValue('');
19239         this.lastSelectionText = '';
19240         
19241     },
19242
19243     /**
19244      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
19245      * will be displayed in the field.  If the value does not match the data value of an existing item,
19246      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
19247      * Otherwise the field will be blank (although the value will still be set).
19248      * @param {String} value The value to match
19249      */
19250     setValue : function(v){
19251         var text = v;
19252         if(this.valueField){
19253             var r = this.findRecord(this.valueField, v);
19254             if(r){
19255                 text = r.data[this.displayField];
19256             }else if(this.valueNotFoundText !== undefined){
19257                 text = this.valueNotFoundText;
19258             }
19259         }
19260         this.lastSelectionText = text;
19261         if(this.hiddenField){
19262             this.hiddenField.value = v;
19263         }
19264         Roo.form.ComboBox.superclass.setValue.call(this, text);
19265         this.value = v;
19266     },
19267     /**
19268      * @property {Object} the last set data for the element
19269      */
19270     
19271     lastData : false,
19272     /**
19273      * Sets the value of the field based on a object which is related to the record format for the store.
19274      * @param {Object} value the value to set as. or false on reset?
19275      */
19276     setFromData : function(o){
19277         var dv = ''; // display value
19278         var vv = ''; // value value..
19279         this.lastData = o;
19280         if (this.displayField) {
19281             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
19282         } else {
19283             // this is an error condition!!!
19284             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
19285         }
19286         
19287         if(this.valueField){
19288             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
19289         }
19290         if(this.hiddenField){
19291             this.hiddenField.value = vv;
19292             
19293             this.lastSelectionText = dv;
19294             Roo.form.ComboBox.superclass.setValue.call(this, dv);
19295             this.value = vv;
19296             return;
19297         }
19298         // no hidden field.. - we store the value in 'value', but still display
19299         // display field!!!!
19300         this.lastSelectionText = dv;
19301         Roo.form.ComboBox.superclass.setValue.call(this, dv);
19302         this.value = vv;
19303         
19304         
19305     },
19306     // private
19307     reset : function(){
19308         // overridden so that last data is reset..
19309         this.setValue(this.resetValue);
19310         this.originalValue = this.getValue();
19311         this.clearInvalid();
19312         this.lastData = false;
19313         if (this.view) {
19314             this.view.clearSelections();
19315         }
19316     },
19317     // private
19318     findRecord : function(prop, value){
19319         var record;
19320         if(this.store.getCount() > 0){
19321             this.store.each(function(r){
19322                 if(r.data[prop] == value){
19323                     record = r;
19324                     return false;
19325                 }
19326                 return true;
19327             });
19328         }
19329         return record;
19330     },
19331     
19332     getName: function()
19333     {
19334         // returns hidden if it's set..
19335         if (!this.rendered) {return ''};
19336         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
19337         
19338     },
19339     // private
19340     onViewMove : function(e, t){
19341         this.inKeyMode = false;
19342     },
19343
19344     // private
19345     onViewOver : function(e, t){
19346         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
19347             return;
19348         }
19349         var item = this.view.findItemFromChild(t);
19350         if(item){
19351             var index = this.view.indexOf(item);
19352             this.select(index, false);
19353         }
19354     },
19355
19356     // private
19357     onViewClick : function(doFocus)
19358     {
19359         var index = this.view.getSelectedIndexes()[0];
19360         var r = this.store.getAt(index);
19361         if(r){
19362             this.onSelect(r, index);
19363         }
19364         if(doFocus !== false && !this.blockFocus){
19365             this.el.focus();
19366         }
19367     },
19368
19369     // private
19370     restrictHeight : function(){
19371         this.innerList.dom.style.height = '';
19372         var inner = this.innerList.dom;
19373         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
19374         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
19375         this.list.beginUpdate();
19376         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
19377         this.list.alignTo(this.el, this.listAlign);
19378         this.list.endUpdate();
19379     },
19380
19381     // private
19382     onEmptyResults : function(){
19383         this.collapse();
19384     },
19385
19386     /**
19387      * Returns true if the dropdown list is expanded, else false.
19388      */
19389     isExpanded : function(){
19390         return this.list.isVisible();
19391     },
19392
19393     /**
19394      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
19395      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19396      * @param {String} value The data value of the item to select
19397      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19398      * selected item if it is not currently in view (defaults to true)
19399      * @return {Boolean} True if the value matched an item in the list, else false
19400      */
19401     selectByValue : function(v, scrollIntoView){
19402         if(v !== undefined && v !== null){
19403             var r = this.findRecord(this.valueField || this.displayField, v);
19404             if(r){
19405                 this.select(this.store.indexOf(r), scrollIntoView);
19406                 return true;
19407             }
19408         }
19409         return false;
19410     },
19411
19412     /**
19413      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
19414      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19415      * @param {Number} index The zero-based index of the list item to select
19416      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19417      * selected item if it is not currently in view (defaults to true)
19418      */
19419     select : function(index, scrollIntoView){
19420         this.selectedIndex = index;
19421         this.view.select(index);
19422         if(scrollIntoView !== false){
19423             var el = this.view.getNode(index);
19424             if(el){
19425                 this.innerList.scrollChildIntoView(el, false);
19426             }
19427         }
19428     },
19429
19430     // private
19431     selectNext : function(){
19432         var ct = this.store.getCount();
19433         if(ct > 0){
19434             if(this.selectedIndex == -1){
19435                 this.select(0);
19436             }else if(this.selectedIndex < ct-1){
19437                 this.select(this.selectedIndex+1);
19438             }
19439         }
19440     },
19441
19442     // private
19443     selectPrev : function(){
19444         var ct = this.store.getCount();
19445         if(ct > 0){
19446             if(this.selectedIndex == -1){
19447                 this.select(0);
19448             }else if(this.selectedIndex != 0){
19449                 this.select(this.selectedIndex-1);
19450             }
19451         }
19452     },
19453
19454     // private
19455     onKeyUp : function(e){
19456         if(this.editable !== false && !e.isSpecialKey()){
19457             this.lastKey = e.getKey();
19458             this.dqTask.delay(this.queryDelay);
19459         }
19460     },
19461
19462     // private
19463     validateBlur : function(){
19464         return !this.list || !this.list.isVisible();   
19465     },
19466
19467     // private
19468     initQuery : function(){
19469         this.doQuery(this.getRawValue());
19470     },
19471
19472     // private
19473     doForce : function(){
19474         if(this.el.dom.value.length > 0){
19475             this.el.dom.value =
19476                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
19477              
19478         }
19479     },
19480
19481     /**
19482      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
19483      * query allowing the query action to be canceled if needed.
19484      * @param {String} query The SQL query to execute
19485      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
19486      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
19487      * saved in the current store (defaults to false)
19488      */
19489     doQuery : function(q, forceAll){
19490         if(q === undefined || q === null){
19491             q = '';
19492         }
19493         var qe = {
19494             query: q,
19495             forceAll: forceAll,
19496             combo: this,
19497             cancel:false
19498         };
19499         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
19500             return false;
19501         }
19502         q = qe.query;
19503         forceAll = qe.forceAll;
19504         if(forceAll === true || (q.length >= this.minChars)){
19505             if(this.lastQuery != q || this.alwaysQuery){
19506                 this.lastQuery = q;
19507                 if(this.mode == 'local'){
19508                     this.selectedIndex = -1;
19509                     if(forceAll){
19510                         this.store.clearFilter();
19511                     }else{
19512                         this.store.filter(this.displayField, q);
19513                     }
19514                     this.onLoad();
19515                 }else{
19516                     this.store.baseParams[this.queryParam] = q;
19517                     this.store.load({
19518                         params: this.getParams(q)
19519                     });
19520                     this.expand();
19521                 }
19522             }else{
19523                 this.selectedIndex = -1;
19524                 this.onLoad();   
19525             }
19526         }
19527     },
19528
19529     // private
19530     getParams : function(q){
19531         var p = {};
19532         //p[this.queryParam] = q;
19533         if(this.pageSize){
19534             p.start = 0;
19535             p.limit = this.pageSize;
19536         }
19537         return p;
19538     },
19539
19540     /**
19541      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
19542      */
19543     collapse : function(){
19544         if(!this.isExpanded()){
19545             return;
19546         }
19547         this.list.hide();
19548         Roo.get(document).un('mousedown', this.collapseIf, this);
19549         Roo.get(document).un('mousewheel', this.collapseIf, this);
19550         if (!this.editable) {
19551             Roo.get(document).un('keydown', this.listKeyPress, this);
19552         }
19553         this.fireEvent('collapse', this);
19554     },
19555
19556     // private
19557     collapseIf : function(e){
19558         if(!e.within(this.wrap) && !e.within(this.list)){
19559             this.collapse();
19560         }
19561     },
19562
19563     /**
19564      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
19565      */
19566     expand : function(){
19567         if(this.isExpanded() || !this.hasFocus){
19568             return;
19569         }
19570         this.list.alignTo(this.el, this.listAlign);
19571         this.list.show();
19572         Roo.get(document).on('mousedown', this.collapseIf, this);
19573         Roo.get(document).on('mousewheel', this.collapseIf, this);
19574         if (!this.editable) {
19575             Roo.get(document).on('keydown', this.listKeyPress, this);
19576         }
19577         
19578         this.fireEvent('expand', this);
19579     },
19580
19581     // private
19582     // Implements the default empty TriggerField.onTriggerClick function
19583     onTriggerClick : function(){
19584         if(this.disabled){
19585             return;
19586         }
19587         if(this.isExpanded()){
19588             this.collapse();
19589             if (!this.blockFocus) {
19590                 this.el.focus();
19591             }
19592             
19593         }else {
19594             this.hasFocus = true;
19595             if(this.triggerAction == 'all') {
19596                 this.doQuery(this.allQuery, true);
19597             } else {
19598                 this.doQuery(this.getRawValue());
19599             }
19600             if (!this.blockFocus) {
19601                 this.el.focus();
19602             }
19603         }
19604     },
19605     listKeyPress : function(e)
19606     {
19607         //Roo.log('listkeypress');
19608         // scroll to first matching element based on key pres..
19609         if (e.isSpecialKey()) {
19610             return false;
19611         }
19612         var k = String.fromCharCode(e.getKey()).toUpperCase();
19613         //Roo.log(k);
19614         var match  = false;
19615         var csel = this.view.getSelectedNodes();
19616         var cselitem = false;
19617         if (csel.length) {
19618             var ix = this.view.indexOf(csel[0]);
19619             cselitem  = this.store.getAt(ix);
19620             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
19621                 cselitem = false;
19622             }
19623             
19624         }
19625         
19626         this.store.each(function(v) { 
19627             if (cselitem) {
19628                 // start at existing selection.
19629                 if (cselitem.id == v.id) {
19630                     cselitem = false;
19631                 }
19632                 return;
19633             }
19634                 
19635             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
19636                 match = this.store.indexOf(v);
19637                 return false;
19638             }
19639         }, this);
19640         
19641         if (match === false) {
19642             return true; // no more action?
19643         }
19644         // scroll to?
19645         this.view.select(match);
19646         var sn = Roo.get(this.view.getSelectedNodes()[0]);
19647         sn.scrollIntoView(sn.dom.parentNode, false);
19648     }
19649
19650     /** 
19651     * @cfg {Boolean} grow 
19652     * @hide 
19653     */
19654     /** 
19655     * @cfg {Number} growMin 
19656     * @hide 
19657     */
19658     /** 
19659     * @cfg {Number} growMax 
19660     * @hide 
19661     */
19662     /**
19663      * @hide
19664      * @method autoSize
19665      */
19666 });/*
19667  * Copyright(c) 2010-2012, Roo J Solutions Limited
19668  *
19669  * Licence LGPL
19670  *
19671  */
19672
19673 /**
19674  * @class Roo.form.ComboBoxArray
19675  * @extends Roo.form.TextField
19676  * A facebook style adder... for lists of email / people / countries  etc...
19677  * pick multiple items from a combo box, and shows each one.
19678  *
19679  *  Fred [x]  Brian [x]  [Pick another |v]
19680  *
19681  *
19682  *  For this to work: it needs various extra information
19683  *    - normal combo problay has
19684  *      name, hiddenName
19685  *    + displayField, valueField
19686  *
19687  *    For our purpose...
19688  *
19689  *
19690  *   If we change from 'extends' to wrapping...
19691  *   
19692  *  
19693  *
19694  
19695  
19696  * @constructor
19697  * Create a new ComboBoxArray.
19698  * @param {Object} config Configuration options
19699  */
19700  
19701
19702 Roo.form.ComboBoxArray = function(config)
19703 {
19704     this.addEvents({
19705         /**
19706          * @event beforeremove
19707          * Fires before remove the value from the list
19708              * @param {Roo.form.ComboBoxArray} _self This combo box array
19709              * @param {Roo.form.ComboBoxArray.Item} item removed item
19710              */
19711         'beforeremove' : true,
19712         /**
19713          * @event remove
19714          * Fires when remove the value from the list
19715              * @param {Roo.form.ComboBoxArray} _self This combo box array
19716              * @param {Roo.form.ComboBoxArray.Item} item removed item
19717              */
19718         'remove' : true
19719         
19720         
19721     });
19722     
19723     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
19724     
19725     this.items = new Roo.util.MixedCollection(false);
19726     
19727     // construct the child combo...
19728     
19729     
19730     
19731     
19732    
19733     
19734 }
19735
19736  
19737 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
19738
19739     /**
19740      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
19741      */
19742     
19743     lastData : false,
19744     
19745     // behavies liek a hiddne field
19746     inputType:      'hidden',
19747     /**
19748      * @cfg {Number} width The width of the box that displays the selected element
19749      */ 
19750     width:          300,
19751
19752     
19753     
19754     /**
19755      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
19756      */
19757     name : false,
19758     /**
19759      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
19760      */
19761     hiddenName : false,
19762     
19763     
19764     // private the array of items that are displayed..
19765     items  : false,
19766     // private - the hidden field el.
19767     hiddenEl : false,
19768     // private - the filed el..
19769     el : false,
19770     
19771     //validateValue : function() { return true; }, // all values are ok!
19772     //onAddClick: function() { },
19773     
19774     onRender : function(ct, position) 
19775     {
19776         
19777         // create the standard hidden element
19778         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
19779         
19780         
19781         // give fake names to child combo;
19782         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
19783         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
19784         
19785         this.combo = Roo.factory(this.combo, Roo.form);
19786         this.combo.onRender(ct, position);
19787         if (typeof(this.combo.width) != 'undefined') {
19788             this.combo.onResize(this.combo.width,0);
19789         }
19790         
19791         this.combo.initEvents();
19792         
19793         // assigned so form know we need to do this..
19794         this.store          = this.combo.store;
19795         this.valueField     = this.combo.valueField;
19796         this.displayField   = this.combo.displayField ;
19797         
19798         
19799         this.combo.wrap.addClass('x-cbarray-grp');
19800         
19801         var cbwrap = this.combo.wrap.createChild(
19802             {tag: 'div', cls: 'x-cbarray-cb'},
19803             this.combo.el.dom
19804         );
19805         
19806              
19807         this.hiddenEl = this.combo.wrap.createChild({
19808             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
19809         });
19810         this.el = this.combo.wrap.createChild({
19811             tag: 'input',  type:'hidden' , name: this.name, value : ''
19812         });
19813          //   this.el.dom.removeAttribute("name");
19814         
19815         
19816         this.outerWrap = this.combo.wrap;
19817         this.wrap = cbwrap;
19818         
19819         this.outerWrap.setWidth(this.width);
19820         this.outerWrap.dom.removeChild(this.el.dom);
19821         
19822         this.wrap.dom.appendChild(this.el.dom);
19823         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
19824         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
19825         
19826         this.combo.trigger.setStyle('position','relative');
19827         this.combo.trigger.setStyle('left', '0px');
19828         this.combo.trigger.setStyle('top', '2px');
19829         
19830         this.combo.el.setStyle('vertical-align', 'text-bottom');
19831         
19832         //this.trigger.setStyle('vertical-align', 'top');
19833         
19834         // this should use the code from combo really... on('add' ....)
19835         if (this.adder) {
19836             
19837         
19838             this.adder = this.outerWrap.createChild(
19839                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
19840             var _t = this;
19841             this.adder.on('click', function(e) {
19842                 _t.fireEvent('adderclick', this, e);
19843             }, _t);
19844         }
19845         //var _t = this;
19846         //this.adder.on('click', this.onAddClick, _t);
19847         
19848         
19849         this.combo.on('select', function(cb, rec, ix) {
19850             this.addItem(rec.data);
19851             
19852             cb.setValue('');
19853             cb.el.dom.value = '';
19854             //cb.lastData = rec.data;
19855             // add to list
19856             
19857         }, this);
19858         
19859         
19860     },
19861     
19862     
19863     getName: function()
19864     {
19865         // returns hidden if it's set..
19866         if (!this.rendered) {return ''};
19867         return  this.hiddenName ? this.hiddenName : this.name;
19868         
19869     },
19870     
19871     
19872     onResize: function(w, h){
19873         
19874         return;
19875         // not sure if this is needed..
19876         //this.combo.onResize(w,h);
19877         
19878         if(typeof w != 'number'){
19879             // we do not handle it!?!?
19880             return;
19881         }
19882         var tw = this.combo.trigger.getWidth();
19883         tw += this.addicon ? this.addicon.getWidth() : 0;
19884         tw += this.editicon ? this.editicon.getWidth() : 0;
19885         var x = w - tw;
19886         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
19887             
19888         this.combo.trigger.setStyle('left', '0px');
19889         
19890         if(this.list && this.listWidth === undefined){
19891             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
19892             this.list.setWidth(lw);
19893             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19894         }
19895         
19896     
19897         
19898     },
19899     
19900     addItem: function(rec)
19901     {
19902         var valueField = this.combo.valueField;
19903         var displayField = this.combo.displayField;
19904         if (this.items.indexOfKey(rec[valueField]) > -1) {
19905             //console.log("GOT " + rec.data.id);
19906             return;
19907         }
19908         
19909         var x = new Roo.form.ComboBoxArray.Item({
19910             //id : rec[this.idField],
19911             data : rec,
19912             displayField : displayField ,
19913             tipField : displayField ,
19914             cb : this
19915         });
19916         // use the 
19917         this.items.add(rec[valueField],x);
19918         // add it before the element..
19919         this.updateHiddenEl();
19920         x.render(this.outerWrap, this.wrap.dom);
19921         // add the image handler..
19922     },
19923     
19924     updateHiddenEl : function()
19925     {
19926         this.validate();
19927         if (!this.hiddenEl) {
19928             return;
19929         }
19930         var ar = [];
19931         var idField = this.combo.valueField;
19932         
19933         this.items.each(function(f) {
19934             ar.push(f.data[idField]);
19935            
19936         });
19937         this.hiddenEl.dom.value = ar.join(',');
19938         this.validate();
19939     },
19940     
19941     reset : function()
19942     {
19943         this.items.clear();
19944         
19945         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
19946            el.remove();
19947         });
19948         
19949         this.el.dom.value = '';
19950         if (this.hiddenEl) {
19951             this.hiddenEl.dom.value = '';
19952         }
19953         
19954     },
19955     getValue: function()
19956     {
19957         return this.hiddenEl ? this.hiddenEl.dom.value : '';
19958     },
19959     setValue: function(v) // not a valid action - must use addItems..
19960     {
19961          
19962         this.reset();
19963         
19964         
19965         
19966         if (this.store.isLocal && (typeof(v) == 'string')) {
19967             // then we can use the store to find the values..
19968             // comma seperated at present.. this needs to allow JSON based encoding..
19969             this.hiddenEl.value  = v;
19970             var v_ar = [];
19971             Roo.each(v.split(','), function(k) {
19972                 Roo.log("CHECK " + this.valueField + ',' + k);
19973                 var li = this.store.query(this.valueField, k);
19974                 if (!li.length) {
19975                     return;
19976                 }
19977                 var add = {};
19978                 add[this.valueField] = k;
19979                 add[this.displayField] = li.item(0).data[this.displayField];
19980                 
19981                 this.addItem(add);
19982             }, this) 
19983              
19984         }
19985         if (typeof(v) == 'object' ) {
19986             // then let's assume it's an array of objects..
19987             Roo.each(v, function(l) {
19988                 this.addItem(l);
19989             }, this);
19990              
19991         }
19992         
19993         
19994     },
19995     setFromData: function(v)
19996     {
19997         // this recieves an object, if setValues is called.
19998         this.reset();
19999         this.el.dom.value = v[this.displayField];
20000         this.hiddenEl.dom.value = v[this.valueField];
20001         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
20002             return;
20003         }
20004         var kv = v[this.valueField];
20005         var dv = v[this.displayField];
20006         kv = typeof(kv) != 'string' ? '' : kv;
20007         dv = typeof(dv) != 'string' ? '' : dv;
20008         
20009         
20010         var keys = kv.split(',');
20011         var display = dv.split(',');
20012         for (var i = 0 ; i < keys.length; i++) {
20013             
20014             add = {};
20015             add[this.valueField] = keys[i];
20016             add[this.displayField] = display[i];
20017             this.addItem(add);
20018         }
20019       
20020         
20021     },
20022     
20023     /**
20024      * Validates the combox array value
20025      * @return {Boolean} True if the value is valid, else false
20026      */
20027     validate : function(){
20028         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
20029             this.clearInvalid();
20030             return true;
20031         }
20032         return false;
20033     },
20034     
20035     validateValue : function(value){
20036         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
20037         
20038     },
20039     
20040     /*@
20041      * overide
20042      * 
20043      */
20044     isDirty : function() {
20045         if(this.disabled) {
20046             return false;
20047         }
20048         
20049         try {
20050             var d = Roo.decode(String(this.originalValue));
20051         } catch (e) {
20052             return String(this.getValue()) !== String(this.originalValue);
20053         }
20054         
20055         var originalValue = [];
20056         
20057         for (var i = 0; i < d.length; i++){
20058             originalValue.push(d[i][this.valueField]);
20059         }
20060         
20061         return String(this.getValue()) !== String(originalValue.join(','));
20062         
20063     }
20064     
20065 });
20066
20067
20068
20069 /**
20070  * @class Roo.form.ComboBoxArray.Item
20071  * @extends Roo.BoxComponent
20072  * A selected item in the list
20073  *  Fred [x]  Brian [x]  [Pick another |v]
20074  * 
20075  * @constructor
20076  * Create a new item.
20077  * @param {Object} config Configuration options
20078  */
20079  
20080 Roo.form.ComboBoxArray.Item = function(config) {
20081     config.id = Roo.id();
20082     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
20083 }
20084
20085 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
20086     data : {},
20087     cb: false,
20088     displayField : false,
20089     tipField : false,
20090     
20091     
20092     defaultAutoCreate : {
20093         tag: 'div',
20094         cls: 'x-cbarray-item',
20095         cn : [ 
20096             { tag: 'div' },
20097             {
20098                 tag: 'img',
20099                 width:16,
20100                 height : 16,
20101                 src : Roo.BLANK_IMAGE_URL ,
20102                 align: 'center'
20103             }
20104         ]
20105         
20106     },
20107     
20108  
20109     onRender : function(ct, position)
20110     {
20111         Roo.form.Field.superclass.onRender.call(this, ct, position);
20112         
20113         if(!this.el){
20114             var cfg = this.getAutoCreate();
20115             this.el = ct.createChild(cfg, position);
20116         }
20117         
20118         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
20119         
20120         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
20121             this.cb.renderer(this.data) :
20122             String.format('{0}',this.data[this.displayField]);
20123         
20124             
20125         this.el.child('div').dom.setAttribute('qtip',
20126                         String.format('{0}',this.data[this.tipField])
20127         );
20128         
20129         this.el.child('img').on('click', this.remove, this);
20130         
20131     },
20132    
20133     remove : function()
20134     {
20135         if(this.cb.disabled){
20136             return;
20137         }
20138         
20139         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
20140             this.cb.items.remove(this);
20141             this.el.child('img').un('click', this.remove, this);
20142             this.el.remove();
20143             this.cb.updateHiddenEl();
20144
20145             this.cb.fireEvent('remove', this.cb, this);
20146         }
20147         
20148     }
20149 });/*
20150  * Based on:
20151  * Ext JS Library 1.1.1
20152  * Copyright(c) 2006-2007, Ext JS, LLC.
20153  *
20154  * Originally Released Under LGPL - original licence link has changed is not relivant.
20155  *
20156  * Fork - LGPL
20157  * <script type="text/javascript">
20158  */
20159 /**
20160  * @class Roo.form.Checkbox
20161  * @extends Roo.form.Field
20162  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
20163  * @constructor
20164  * Creates a new Checkbox
20165  * @param {Object} config Configuration options
20166  */
20167 Roo.form.Checkbox = function(config){
20168     Roo.form.Checkbox.superclass.constructor.call(this, config);
20169     this.addEvents({
20170         /**
20171          * @event check
20172          * Fires when the checkbox is checked or unchecked.
20173              * @param {Roo.form.Checkbox} this This checkbox
20174              * @param {Boolean} checked The new checked value
20175              */
20176         check : true
20177     });
20178 };
20179
20180 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
20181     /**
20182      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
20183      */
20184     focusClass : undefined,
20185     /**
20186      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
20187      */
20188     fieldClass: "x-form-field",
20189     /**
20190      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
20191      */
20192     checked: false,
20193     /**
20194      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20195      * {tag: "input", type: "checkbox", autocomplete: "off"})
20196      */
20197     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
20198     /**
20199      * @cfg {String} boxLabel The text that appears beside the checkbox
20200      */
20201     boxLabel : "",
20202     /**
20203      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
20204      */  
20205     inputValue : '1',
20206     /**
20207      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20208      */
20209      valueOff: '0', // value when not checked..
20210
20211     actionMode : 'viewEl', 
20212     //
20213     // private
20214     itemCls : 'x-menu-check-item x-form-item',
20215     groupClass : 'x-menu-group-item',
20216     inputType : 'hidden',
20217     
20218     
20219     inSetChecked: false, // check that we are not calling self...
20220     
20221     inputElement: false, // real input element?
20222     basedOn: false, // ????
20223     
20224     isFormField: true, // not sure where this is needed!!!!
20225
20226     onResize : function(){
20227         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
20228         if(!this.boxLabel){
20229             this.el.alignTo(this.wrap, 'c-c');
20230         }
20231     },
20232
20233     initEvents : function(){
20234         Roo.form.Checkbox.superclass.initEvents.call(this);
20235         this.el.on("click", this.onClick,  this);
20236         this.el.on("change", this.onClick,  this);
20237     },
20238
20239
20240     getResizeEl : function(){
20241         return this.wrap;
20242     },
20243
20244     getPositionEl : function(){
20245         return this.wrap;
20246     },
20247
20248     // private
20249     onRender : function(ct, position){
20250         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20251         /*
20252         if(this.inputValue !== undefined){
20253             this.el.dom.value = this.inputValue;
20254         }
20255         */
20256         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20257         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20258         var viewEl = this.wrap.createChild({ 
20259             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20260         this.viewEl = viewEl;   
20261         this.wrap.on('click', this.onClick,  this); 
20262         
20263         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20264         this.el.on('propertychange', this.setFromHidden,  this);  //ie
20265         
20266         
20267         
20268         if(this.boxLabel){
20269             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20270         //    viewEl.on('click', this.onClick,  this); 
20271         }
20272         //if(this.checked){
20273             this.setChecked(this.checked);
20274         //}else{
20275             //this.checked = this.el.dom;
20276         //}
20277
20278     },
20279
20280     // private
20281     initValue : Roo.emptyFn,
20282
20283     /**
20284      * Returns the checked state of the checkbox.
20285      * @return {Boolean} True if checked, else false
20286      */
20287     getValue : function(){
20288         if(this.el){
20289             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
20290         }
20291         return this.valueOff;
20292         
20293     },
20294
20295         // private
20296     onClick : function(){ 
20297         if (this.disabled) {
20298             return;
20299         }
20300         this.setChecked(!this.checked);
20301
20302         //if(this.el.dom.checked != this.checked){
20303         //    this.setValue(this.el.dom.checked);
20304        // }
20305     },
20306
20307     /**
20308      * Sets the checked state of the checkbox.
20309      * On is always based on a string comparison between inputValue and the param.
20310      * @param {Boolean/String} value - the value to set 
20311      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
20312      */
20313     setValue : function(v,suppressEvent){
20314         
20315         
20316         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
20317         //if(this.el && this.el.dom){
20318         //    this.el.dom.checked = this.checked;
20319         //    this.el.dom.defaultChecked = this.checked;
20320         //}
20321         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
20322         //this.fireEvent("check", this, this.checked);
20323     },
20324     // private..
20325     setChecked : function(state,suppressEvent)
20326     {
20327         if (this.inSetChecked) {
20328             this.checked = state;
20329             return;
20330         }
20331         
20332     
20333         if(this.wrap){
20334             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
20335         }
20336         this.checked = state;
20337         if(suppressEvent !== true){
20338             this.fireEvent('check', this, state);
20339         }
20340         this.inSetChecked = true;
20341         this.el.dom.value = state ? this.inputValue : this.valueOff;
20342         this.inSetChecked = false;
20343         
20344     },
20345     // handle setting of hidden value by some other method!!?!?
20346     setFromHidden: function()
20347     {
20348         if(!this.el){
20349             return;
20350         }
20351         //console.log("SET FROM HIDDEN");
20352         //alert('setFrom hidden');
20353         this.setValue(this.el.dom.value);
20354     },
20355     
20356     onDestroy : function()
20357     {
20358         if(this.viewEl){
20359             Roo.get(this.viewEl).remove();
20360         }
20361          
20362         Roo.form.Checkbox.superclass.onDestroy.call(this);
20363     },
20364     
20365     setBoxLabel : function(str)
20366     {
20367         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
20368     }
20369
20370 });/*
20371  * Based on:
20372  * Ext JS Library 1.1.1
20373  * Copyright(c) 2006-2007, Ext JS, LLC.
20374  *
20375  * Originally Released Under LGPL - original licence link has changed is not relivant.
20376  *
20377  * Fork - LGPL
20378  * <script type="text/javascript">
20379  */
20380  
20381 /**
20382  * @class Roo.form.Radio
20383  * @extends Roo.form.Checkbox
20384  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
20385  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
20386  * @constructor
20387  * Creates a new Radio
20388  * @param {Object} config Configuration options
20389  */
20390 Roo.form.Radio = function(){
20391     Roo.form.Radio.superclass.constructor.apply(this, arguments);
20392 };
20393 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
20394     inputType: 'radio',
20395
20396     /**
20397      * If this radio is part of a group, it will return the selected value
20398      * @return {String}
20399      */
20400     getGroupValue : function(){
20401         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
20402     },
20403     
20404     
20405     onRender : function(ct, position){
20406         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20407         
20408         if(this.inputValue !== undefined){
20409             this.el.dom.value = this.inputValue;
20410         }
20411          
20412         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20413         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20414         //var viewEl = this.wrap.createChild({ 
20415         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20416         //this.viewEl = viewEl;   
20417         //this.wrap.on('click', this.onClick,  this); 
20418         
20419         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20420         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
20421         
20422         
20423         
20424         if(this.boxLabel){
20425             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20426         //    viewEl.on('click', this.onClick,  this); 
20427         }
20428          if(this.checked){
20429             this.el.dom.checked =   'checked' ;
20430         }
20431          
20432     } 
20433     
20434     
20435 });//<script type="text/javascript">
20436
20437 /*
20438  * Based  Ext JS Library 1.1.1
20439  * Copyright(c) 2006-2007, Ext JS, LLC.
20440  * LGPL
20441  *
20442  */
20443  
20444 /**
20445  * @class Roo.HtmlEditorCore
20446  * @extends Roo.Component
20447  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20448  *
20449  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20450  */
20451
20452 Roo.HtmlEditorCore = function(config){
20453     
20454     
20455     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20456     
20457     
20458     this.addEvents({
20459         /**
20460          * @event initialize
20461          * Fires when the editor is fully initialized (including the iframe)
20462          * @param {Roo.HtmlEditorCore} this
20463          */
20464         initialize: true,
20465         /**
20466          * @event activate
20467          * Fires when the editor is first receives the focus. Any insertion must wait
20468          * until after this event.
20469          * @param {Roo.HtmlEditorCore} this
20470          */
20471         activate: true,
20472          /**
20473          * @event beforesync
20474          * Fires before the textarea is updated with content from the editor iframe. Return false
20475          * to cancel the sync.
20476          * @param {Roo.HtmlEditorCore} this
20477          * @param {String} html
20478          */
20479         beforesync: true,
20480          /**
20481          * @event beforepush
20482          * Fires before the iframe editor is updated with content from the textarea. Return false
20483          * to cancel the push.
20484          * @param {Roo.HtmlEditorCore} this
20485          * @param {String} html
20486          */
20487         beforepush: true,
20488          /**
20489          * @event sync
20490          * Fires when the textarea is updated with content from the editor iframe.
20491          * @param {Roo.HtmlEditorCore} this
20492          * @param {String} html
20493          */
20494         sync: true,
20495          /**
20496          * @event push
20497          * Fires when the iframe editor is updated with content from the textarea.
20498          * @param {Roo.HtmlEditorCore} this
20499          * @param {String} html
20500          */
20501         push: true,
20502         
20503         /**
20504          * @event editorevent
20505          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20506          * @param {Roo.HtmlEditorCore} this
20507          */
20508         editorevent: true
20509         
20510     });
20511     
20512     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20513     
20514     // defaults : white / black...
20515     this.applyBlacklists();
20516     
20517     
20518     
20519 };
20520
20521
20522 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
20523
20524
20525      /**
20526      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
20527      */
20528     
20529     owner : false,
20530     
20531      /**
20532      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20533      *                        Roo.resizable.
20534      */
20535     resizable : false,
20536      /**
20537      * @cfg {Number} height (in pixels)
20538      */   
20539     height: 300,
20540    /**
20541      * @cfg {Number} width (in pixels)
20542      */   
20543     width: 500,
20544     
20545     /**
20546      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20547      * 
20548      */
20549     stylesheets: false,
20550     
20551     // id of frame..
20552     frameId: false,
20553     
20554     // private properties
20555     validationEvent : false,
20556     deferHeight: true,
20557     initialized : false,
20558     activated : false,
20559     sourceEditMode : false,
20560     onFocus : Roo.emptyFn,
20561     iframePad:3,
20562     hideMode:'offsets',
20563     
20564     clearUp: true,
20565     
20566     // blacklist + whitelisted elements..
20567     black: false,
20568     white: false,
20569      
20570     bodyCls : '',
20571
20572     /**
20573      * Protected method that will not generally be called directly. It
20574      * is called when the editor initializes the iframe with HTML contents. Override this method if you
20575      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20576      */
20577     getDocMarkup : function(){
20578         // body styles..
20579         var st = '';
20580         
20581         // inherit styels from page...?? 
20582         if (this.stylesheets === false) {
20583             
20584             Roo.get(document.head).select('style').each(function(node) {
20585                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20586             });
20587             
20588             Roo.get(document.head).select('link').each(function(node) { 
20589                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20590             });
20591             
20592         } else if (!this.stylesheets.length) {
20593                 // simple..
20594                 st = '<style type="text/css">' +
20595                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20596                    '</style>';
20597         } else { 
20598             st = '<style type="text/css">' +
20599                     this.stylesheets +
20600                 '</style>';
20601         }
20602         
20603         st +=  '<style type="text/css">' +
20604             'IMG { cursor: pointer } ' +
20605         '</style>';
20606
20607         var cls = 'roo-htmleditor-body';
20608         
20609         if(this.bodyCls.length){
20610             cls += ' ' + this.bodyCls;
20611         }
20612         
20613         return '<html><head>' + st  +
20614             //<style type="text/css">' +
20615             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20616             //'</style>' +
20617             ' </head><body class="' +  cls + '"></body></html>';
20618     },
20619
20620     // private
20621     onRender : function(ct, position)
20622     {
20623         var _t = this;
20624         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20625         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20626         
20627         
20628         this.el.dom.style.border = '0 none';
20629         this.el.dom.setAttribute('tabIndex', -1);
20630         this.el.addClass('x-hidden hide');
20631         
20632         
20633         
20634         if(Roo.isIE){ // fix IE 1px bogus margin
20635             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20636         }
20637        
20638         
20639         this.frameId = Roo.id();
20640         
20641          
20642         
20643         var iframe = this.owner.wrap.createChild({
20644             tag: 'iframe',
20645             cls: 'form-control', // bootstrap..
20646             id: this.frameId,
20647             name: this.frameId,
20648             frameBorder : 'no',
20649             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
20650         }, this.el
20651         );
20652         
20653         
20654         this.iframe = iframe.dom;
20655
20656          this.assignDocWin();
20657         
20658         this.doc.designMode = 'on';
20659        
20660         this.doc.open();
20661         this.doc.write(this.getDocMarkup());
20662         this.doc.close();
20663
20664         
20665         var task = { // must defer to wait for browser to be ready
20666             run : function(){
20667                 //console.log("run task?" + this.doc.readyState);
20668                 this.assignDocWin();
20669                 if(this.doc.body || this.doc.readyState == 'complete'){
20670                     try {
20671                         this.doc.designMode="on";
20672                     } catch (e) {
20673                         return;
20674                     }
20675                     Roo.TaskMgr.stop(task);
20676                     this.initEditor.defer(10, this);
20677                 }
20678             },
20679             interval : 10,
20680             duration: 10000,
20681             scope: this
20682         };
20683         Roo.TaskMgr.start(task);
20684
20685     },
20686
20687     // private
20688     onResize : function(w, h)
20689     {
20690          Roo.log('resize: ' +w + ',' + h );
20691         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20692         if(!this.iframe){
20693             return;
20694         }
20695         if(typeof w == 'number'){
20696             
20697             this.iframe.style.width = w + 'px';
20698         }
20699         if(typeof h == 'number'){
20700             
20701             this.iframe.style.height = h + 'px';
20702             if(this.doc){
20703                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20704             }
20705         }
20706         
20707     },
20708
20709     /**
20710      * Toggles the editor between standard and source edit mode.
20711      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20712      */
20713     toggleSourceEdit : function(sourceEditMode){
20714         
20715         this.sourceEditMode = sourceEditMode === true;
20716         
20717         if(this.sourceEditMode){
20718  
20719             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
20720             
20721         }else{
20722             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20723             //this.iframe.className = '';
20724             this.deferFocus();
20725         }
20726         //this.setSize(this.owner.wrap.getSize());
20727         //this.fireEvent('editmodechange', this, this.sourceEditMode);
20728     },
20729
20730     
20731   
20732
20733     /**
20734      * Protected method that will not generally be called directly. If you need/want
20735      * custom HTML cleanup, this is the method you should override.
20736      * @param {String} html The HTML to be cleaned
20737      * return {String} The cleaned HTML
20738      */
20739     cleanHtml : function(html){
20740         html = String(html);
20741         if(html.length > 5){
20742             if(Roo.isSafari){ // strip safari nonsense
20743                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20744             }
20745         }
20746         if(html == '&nbsp;'){
20747             html = '';
20748         }
20749         return html;
20750     },
20751
20752     /**
20753      * HTML Editor -> Textarea
20754      * Protected method that will not generally be called directly. Syncs the contents
20755      * of the editor iframe with the textarea.
20756      */
20757     syncValue : function(){
20758         if(this.initialized){
20759             var bd = (this.doc.body || this.doc.documentElement);
20760             //this.cleanUpPaste(); -- this is done else where and causes havoc..
20761             var html = bd.innerHTML;
20762             if(Roo.isSafari){
20763                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20764                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20765                 if(m && m[1]){
20766                     html = '<div style="'+m[0]+'">' + html + '</div>';
20767                 }
20768             }
20769             html = this.cleanHtml(html);
20770             // fix up the special chars.. normaly like back quotes in word...
20771             // however we do not want to do this with chinese..
20772             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
20773                 var cc = b.charCodeAt();
20774                 if (
20775                     (cc >= 0x4E00 && cc < 0xA000 ) ||
20776                     (cc >= 0x3400 && cc < 0x4E00 ) ||
20777                     (cc >= 0xf900 && cc < 0xfb00 )
20778                 ) {
20779                         return b;
20780                 }
20781                 return "&#"+cc+";" 
20782             });
20783             if(this.owner.fireEvent('beforesync', this, html) !== false){
20784                 this.el.dom.value = html;
20785                 this.owner.fireEvent('sync', this, html);
20786             }
20787         }
20788     },
20789
20790     /**
20791      * Protected method that will not generally be called directly. Pushes the value of the textarea
20792      * into the iframe editor.
20793      */
20794     pushValue : function(){
20795         if(this.initialized){
20796             var v = this.el.dom.value.trim();
20797             
20798 //            if(v.length < 1){
20799 //                v = '&#160;';
20800 //            }
20801             
20802             if(this.owner.fireEvent('beforepush', this, v) !== false){
20803                 var d = (this.doc.body || this.doc.documentElement);
20804                 d.innerHTML = v;
20805                 this.cleanUpPaste();
20806                 this.el.dom.value = d.innerHTML;
20807                 this.owner.fireEvent('push', this, v);
20808             }
20809         }
20810     },
20811
20812     // private
20813     deferFocus : function(){
20814         this.focus.defer(10, this);
20815     },
20816
20817     // doc'ed in Field
20818     focus : function(){
20819         if(this.win && !this.sourceEditMode){
20820             this.win.focus();
20821         }else{
20822             this.el.focus();
20823         }
20824     },
20825     
20826     assignDocWin: function()
20827     {
20828         var iframe = this.iframe;
20829         
20830          if(Roo.isIE){
20831             this.doc = iframe.contentWindow.document;
20832             this.win = iframe.contentWindow;
20833         } else {
20834 //            if (!Roo.get(this.frameId)) {
20835 //                return;
20836 //            }
20837 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20838 //            this.win = Roo.get(this.frameId).dom.contentWindow;
20839             
20840             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20841                 return;
20842             }
20843             
20844             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20845             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20846         }
20847     },
20848     
20849     // private
20850     initEditor : function(){
20851         //console.log("INIT EDITOR");
20852         this.assignDocWin();
20853         
20854         
20855         
20856         this.doc.designMode="on";
20857         this.doc.open();
20858         this.doc.write(this.getDocMarkup());
20859         this.doc.close();
20860         
20861         var dbody = (this.doc.body || this.doc.documentElement);
20862         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20863         // this copies styles from the containing element into thsi one..
20864         // not sure why we need all of this..
20865         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20866         
20867         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20868         //ss['background-attachment'] = 'fixed'; // w3c
20869         dbody.bgProperties = 'fixed'; // ie
20870         //Roo.DomHelper.applyStyles(dbody, ss);
20871         Roo.EventManager.on(this.doc, {
20872             //'mousedown': this.onEditorEvent,
20873             'mouseup': this.onEditorEvent,
20874             'dblclick': this.onEditorEvent,
20875             'click': this.onEditorEvent,
20876             'keyup': this.onEditorEvent,
20877             buffer:100,
20878             scope: this
20879         });
20880         if(Roo.isGecko){
20881             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20882         }
20883         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20884             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20885         }
20886         this.initialized = true;
20887
20888         this.owner.fireEvent('initialize', this);
20889         this.pushValue();
20890     },
20891
20892     // private
20893     onDestroy : function(){
20894         
20895         
20896         
20897         if(this.rendered){
20898             
20899             //for (var i =0; i < this.toolbars.length;i++) {
20900             //    // fixme - ask toolbars for heights?
20901             //    this.toolbars[i].onDestroy();
20902            // }
20903             
20904             //this.wrap.dom.innerHTML = '';
20905             //this.wrap.remove();
20906         }
20907     },
20908
20909     // private
20910     onFirstFocus : function(){
20911         
20912         this.assignDocWin();
20913         
20914         
20915         this.activated = true;
20916          
20917     
20918         if(Roo.isGecko){ // prevent silly gecko errors
20919             this.win.focus();
20920             var s = this.win.getSelection();
20921             if(!s.focusNode || s.focusNode.nodeType != 3){
20922                 var r = s.getRangeAt(0);
20923                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
20924                 r.collapse(true);
20925                 this.deferFocus();
20926             }
20927             try{
20928                 this.execCmd('useCSS', true);
20929                 this.execCmd('styleWithCSS', false);
20930             }catch(e){}
20931         }
20932         this.owner.fireEvent('activate', this);
20933     },
20934
20935     // private
20936     adjustFont: function(btn){
20937         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
20938         //if(Roo.isSafari){ // safari
20939         //    adjust *= 2;
20940        // }
20941         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
20942         if(Roo.isSafari){ // safari
20943             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
20944             v =  (v < 10) ? 10 : v;
20945             v =  (v > 48) ? 48 : v;
20946             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
20947             
20948         }
20949         
20950         
20951         v = Math.max(1, v+adjust);
20952         
20953         this.execCmd('FontSize', v  );
20954     },
20955
20956     onEditorEvent : function(e)
20957     {
20958         this.owner.fireEvent('editorevent', this, e);
20959       //  this.updateToolbar();
20960         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
20961     },
20962
20963     insertTag : function(tg)
20964     {
20965         // could be a bit smarter... -> wrap the current selected tRoo..
20966         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
20967             
20968             range = this.createRange(this.getSelection());
20969             var wrappingNode = this.doc.createElement(tg.toLowerCase());
20970             wrappingNode.appendChild(range.extractContents());
20971             range.insertNode(wrappingNode);
20972
20973             return;
20974             
20975             
20976             
20977         }
20978         this.execCmd("formatblock",   tg);
20979         
20980     },
20981     
20982     insertText : function(txt)
20983     {
20984         
20985         
20986         var range = this.createRange();
20987         range.deleteContents();
20988                //alert(Sender.getAttribute('label'));
20989                
20990         range.insertNode(this.doc.createTextNode(txt));
20991     } ,
20992     
20993      
20994
20995     /**
20996      * Executes a Midas editor command on the editor document and performs necessary focus and
20997      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
20998      * @param {String} cmd The Midas command
20999      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21000      */
21001     relayCmd : function(cmd, value){
21002         this.win.focus();
21003         this.execCmd(cmd, value);
21004         this.owner.fireEvent('editorevent', this);
21005         //this.updateToolbar();
21006         this.owner.deferFocus();
21007     },
21008
21009     /**
21010      * Executes a Midas editor command directly on the editor document.
21011      * For visual commands, you should use {@link #relayCmd} instead.
21012      * <b>This should only be called after the editor is initialized.</b>
21013      * @param {String} cmd The Midas command
21014      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21015      */
21016     execCmd : function(cmd, value){
21017         this.doc.execCommand(cmd, false, value === undefined ? null : value);
21018         this.syncValue();
21019     },
21020  
21021  
21022    
21023     /**
21024      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21025      * to insert tRoo.
21026      * @param {String} text | dom node.. 
21027      */
21028     insertAtCursor : function(text)
21029     {
21030         
21031         if(!this.activated){
21032             return;
21033         }
21034         /*
21035         if(Roo.isIE){
21036             this.win.focus();
21037             var r = this.doc.selection.createRange();
21038             if(r){
21039                 r.collapse(true);
21040                 r.pasteHTML(text);
21041                 this.syncValue();
21042                 this.deferFocus();
21043             
21044             }
21045             return;
21046         }
21047         */
21048         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21049             this.win.focus();
21050             
21051             
21052             // from jquery ui (MIT licenced)
21053             var range, node;
21054             var win = this.win;
21055             
21056             if (win.getSelection && win.getSelection().getRangeAt) {
21057                 range = win.getSelection().getRangeAt(0);
21058                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21059                 range.insertNode(node);
21060             } else if (win.document.selection && win.document.selection.createRange) {
21061                 // no firefox support
21062                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21063                 win.document.selection.createRange().pasteHTML(txt);
21064             } else {
21065                 // no firefox support
21066                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21067                 this.execCmd('InsertHTML', txt);
21068             } 
21069             
21070             this.syncValue();
21071             
21072             this.deferFocus();
21073         }
21074     },
21075  // private
21076     mozKeyPress : function(e){
21077         if(e.ctrlKey){
21078             var c = e.getCharCode(), cmd;
21079           
21080             if(c > 0){
21081                 c = String.fromCharCode(c).toLowerCase();
21082                 switch(c){
21083                     case 'b':
21084                         cmd = 'bold';
21085                         break;
21086                     case 'i':
21087                         cmd = 'italic';
21088                         break;
21089                     
21090                     case 'u':
21091                         cmd = 'underline';
21092                         break;
21093                     
21094                     case 'v':
21095                         this.cleanUpPaste.defer(100, this);
21096                         return;
21097                         
21098                 }
21099                 if(cmd){
21100                     this.win.focus();
21101                     this.execCmd(cmd);
21102                     this.deferFocus();
21103                     e.preventDefault();
21104                 }
21105                 
21106             }
21107         }
21108     },
21109
21110     // private
21111     fixKeys : function(){ // load time branching for fastest keydown performance
21112         if(Roo.isIE){
21113             return function(e){
21114                 var k = e.getKey(), r;
21115                 if(k == e.TAB){
21116                     e.stopEvent();
21117                     r = this.doc.selection.createRange();
21118                     if(r){
21119                         r.collapse(true);
21120                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21121                         this.deferFocus();
21122                     }
21123                     return;
21124                 }
21125                 
21126                 if(k == e.ENTER){
21127                     r = this.doc.selection.createRange();
21128                     if(r){
21129                         var target = r.parentElement();
21130                         if(!target || target.tagName.toLowerCase() != 'li'){
21131                             e.stopEvent();
21132                             r.pasteHTML('<br />');
21133                             r.collapse(false);
21134                             r.select();
21135                         }
21136                     }
21137                 }
21138                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21139                     this.cleanUpPaste.defer(100, this);
21140                     return;
21141                 }
21142                 
21143                 
21144             };
21145         }else if(Roo.isOpera){
21146             return function(e){
21147                 var k = e.getKey();
21148                 if(k == e.TAB){
21149                     e.stopEvent();
21150                     this.win.focus();
21151                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21152                     this.deferFocus();
21153                 }
21154                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21155                     this.cleanUpPaste.defer(100, this);
21156                     return;
21157                 }
21158                 
21159             };
21160         }else if(Roo.isSafari){
21161             return function(e){
21162                 var k = e.getKey();
21163                 
21164                 if(k == e.TAB){
21165                     e.stopEvent();
21166                     this.execCmd('InsertText','\t');
21167                     this.deferFocus();
21168                     return;
21169                 }
21170                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21171                     this.cleanUpPaste.defer(100, this);
21172                     return;
21173                 }
21174                 
21175              };
21176         }
21177     }(),
21178     
21179     getAllAncestors: function()
21180     {
21181         var p = this.getSelectedNode();
21182         var a = [];
21183         if (!p) {
21184             a.push(p); // push blank onto stack..
21185             p = this.getParentElement();
21186         }
21187         
21188         
21189         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21190             a.push(p);
21191             p = p.parentNode;
21192         }
21193         a.push(this.doc.body);
21194         return a;
21195     },
21196     lastSel : false,
21197     lastSelNode : false,
21198     
21199     
21200     getSelection : function() 
21201     {
21202         this.assignDocWin();
21203         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21204     },
21205     
21206     getSelectedNode: function() 
21207     {
21208         // this may only work on Gecko!!!
21209         
21210         // should we cache this!!!!
21211         
21212         
21213         
21214          
21215         var range = this.createRange(this.getSelection()).cloneRange();
21216         
21217         if (Roo.isIE) {
21218             var parent = range.parentElement();
21219             while (true) {
21220                 var testRange = range.duplicate();
21221                 testRange.moveToElementText(parent);
21222                 if (testRange.inRange(range)) {
21223                     break;
21224                 }
21225                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21226                     break;
21227                 }
21228                 parent = parent.parentElement;
21229             }
21230             return parent;
21231         }
21232         
21233         // is ancestor a text element.
21234         var ac =  range.commonAncestorContainer;
21235         if (ac.nodeType == 3) {
21236             ac = ac.parentNode;
21237         }
21238         
21239         var ar = ac.childNodes;
21240          
21241         var nodes = [];
21242         var other_nodes = [];
21243         var has_other_nodes = false;
21244         for (var i=0;i<ar.length;i++) {
21245             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
21246                 continue;
21247             }
21248             // fullly contained node.
21249             
21250             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21251                 nodes.push(ar[i]);
21252                 continue;
21253             }
21254             
21255             // probably selected..
21256             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21257                 other_nodes.push(ar[i]);
21258                 continue;
21259             }
21260             // outer..
21261             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
21262                 continue;
21263             }
21264             
21265             
21266             has_other_nodes = true;
21267         }
21268         if (!nodes.length && other_nodes.length) {
21269             nodes= other_nodes;
21270         }
21271         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21272             return false;
21273         }
21274         
21275         return nodes[0];
21276     },
21277     createRange: function(sel)
21278     {
21279         // this has strange effects when using with 
21280         // top toolbar - not sure if it's a great idea.
21281         //this.editor.contentWindow.focus();
21282         if (typeof sel != "undefined") {
21283             try {
21284                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21285             } catch(e) {
21286                 return this.doc.createRange();
21287             }
21288         } else {
21289             return this.doc.createRange();
21290         }
21291     },
21292     getParentElement: function()
21293     {
21294         
21295         this.assignDocWin();
21296         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21297         
21298         var range = this.createRange(sel);
21299          
21300         try {
21301             var p = range.commonAncestorContainer;
21302             while (p.nodeType == 3) { // text node
21303                 p = p.parentNode;
21304             }
21305             return p;
21306         } catch (e) {
21307             return null;
21308         }
21309     
21310     },
21311     /***
21312      *
21313      * Range intersection.. the hard stuff...
21314      *  '-1' = before
21315      *  '0' = hits..
21316      *  '1' = after.
21317      *         [ -- selected range --- ]
21318      *   [fail]                        [fail]
21319      *
21320      *    basically..
21321      *      if end is before start or  hits it. fail.
21322      *      if start is after end or hits it fail.
21323      *
21324      *   if either hits (but other is outside. - then it's not 
21325      *   
21326      *    
21327      **/
21328     
21329     
21330     // @see http://www.thismuchiknow.co.uk/?p=64.
21331     rangeIntersectsNode : function(range, node)
21332     {
21333         var nodeRange = node.ownerDocument.createRange();
21334         try {
21335             nodeRange.selectNode(node);
21336         } catch (e) {
21337             nodeRange.selectNodeContents(node);
21338         }
21339     
21340         var rangeStartRange = range.cloneRange();
21341         rangeStartRange.collapse(true);
21342     
21343         var rangeEndRange = range.cloneRange();
21344         rangeEndRange.collapse(false);
21345     
21346         var nodeStartRange = nodeRange.cloneRange();
21347         nodeStartRange.collapse(true);
21348     
21349         var nodeEndRange = nodeRange.cloneRange();
21350         nodeEndRange.collapse(false);
21351     
21352         return rangeStartRange.compareBoundaryPoints(
21353                  Range.START_TO_START, nodeEndRange) == -1 &&
21354                rangeEndRange.compareBoundaryPoints(
21355                  Range.START_TO_START, nodeStartRange) == 1;
21356         
21357          
21358     },
21359     rangeCompareNode : function(range, node)
21360     {
21361         var nodeRange = node.ownerDocument.createRange();
21362         try {
21363             nodeRange.selectNode(node);
21364         } catch (e) {
21365             nodeRange.selectNodeContents(node);
21366         }
21367         
21368         
21369         range.collapse(true);
21370     
21371         nodeRange.collapse(true);
21372      
21373         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21374         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
21375          
21376         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21377         
21378         var nodeIsBefore   =  ss == 1;
21379         var nodeIsAfter    = ee == -1;
21380         
21381         if (nodeIsBefore && nodeIsAfter) {
21382             return 0; // outer
21383         }
21384         if (!nodeIsBefore && nodeIsAfter) {
21385             return 1; //right trailed.
21386         }
21387         
21388         if (nodeIsBefore && !nodeIsAfter) {
21389             return 2;  // left trailed.
21390         }
21391         // fully contined.
21392         return 3;
21393     },
21394
21395     // private? - in a new class?
21396     cleanUpPaste :  function()
21397     {
21398         // cleans up the whole document..
21399         Roo.log('cleanuppaste');
21400         
21401         this.cleanUpChildren(this.doc.body);
21402         var clean = this.cleanWordChars(this.doc.body.innerHTML);
21403         if (clean != this.doc.body.innerHTML) {
21404             this.doc.body.innerHTML = clean;
21405         }
21406         
21407     },
21408     
21409     cleanWordChars : function(input) {// change the chars to hex code
21410         var he = Roo.HtmlEditorCore;
21411         
21412         var output = input;
21413         Roo.each(he.swapCodes, function(sw) { 
21414             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21415             
21416             output = output.replace(swapper, sw[1]);
21417         });
21418         
21419         return output;
21420     },
21421     
21422     
21423     cleanUpChildren : function (n)
21424     {
21425         if (!n.childNodes.length) {
21426             return;
21427         }
21428         for (var i = n.childNodes.length-1; i > -1 ; i--) {
21429            this.cleanUpChild(n.childNodes[i]);
21430         }
21431     },
21432     
21433     
21434         
21435     
21436     cleanUpChild : function (node)
21437     {
21438         var ed = this;
21439         //console.log(node);
21440         if (node.nodeName == "#text") {
21441             // clean up silly Windows -- stuff?
21442             return; 
21443         }
21444         if (node.nodeName == "#comment") {
21445             node.parentNode.removeChild(node);
21446             // clean up silly Windows -- stuff?
21447             return; 
21448         }
21449         var lcname = node.tagName.toLowerCase();
21450         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21451         // whitelist of tags..
21452         
21453         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21454             // remove node.
21455             node.parentNode.removeChild(node);
21456             return;
21457             
21458         }
21459         
21460         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21461         
21462         // remove <a name=....> as rendering on yahoo mailer is borked with this.
21463         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21464         
21465         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21466         //    remove_keep_children = true;
21467         //}
21468         
21469         if (remove_keep_children) {
21470             this.cleanUpChildren(node);
21471             // inserts everything just before this node...
21472             while (node.childNodes.length) {
21473                 var cn = node.childNodes[0];
21474                 node.removeChild(cn);
21475                 node.parentNode.insertBefore(cn, node);
21476             }
21477             node.parentNode.removeChild(node);
21478             return;
21479         }
21480         
21481         if (!node.attributes || !node.attributes.length) {
21482             this.cleanUpChildren(node);
21483             return;
21484         }
21485         
21486         function cleanAttr(n,v)
21487         {
21488             
21489             if (v.match(/^\./) || v.match(/^\//)) {
21490                 return;
21491             }
21492             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
21493                 return;
21494             }
21495             if (v.match(/^#/)) {
21496                 return;
21497             }
21498 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21499             node.removeAttribute(n);
21500             
21501         }
21502         
21503         var cwhite = this.cwhite;
21504         var cblack = this.cblack;
21505             
21506         function cleanStyle(n,v)
21507         {
21508             if (v.match(/expression/)) { //XSS?? should we even bother..
21509                 node.removeAttribute(n);
21510                 return;
21511             }
21512             
21513             var parts = v.split(/;/);
21514             var clean = [];
21515             
21516             Roo.each(parts, function(p) {
21517                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21518                 if (!p.length) {
21519                     return true;
21520                 }
21521                 var l = p.split(':').shift().replace(/\s+/g,'');
21522                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21523                 
21524                 if ( cwhite.length && cblack.indexOf(l) > -1) {
21525 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21526                     //node.removeAttribute(n);
21527                     return true;
21528                 }
21529                 //Roo.log()
21530                 // only allow 'c whitelisted system attributes'
21531                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
21532 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21533                     //node.removeAttribute(n);
21534                     return true;
21535                 }
21536                 
21537                 
21538                  
21539                 
21540                 clean.push(p);
21541                 return true;
21542             });
21543             if (clean.length) { 
21544                 node.setAttribute(n, clean.join(';'));
21545             } else {
21546                 node.removeAttribute(n);
21547             }
21548             
21549         }
21550         
21551         
21552         for (var i = node.attributes.length-1; i > -1 ; i--) {
21553             var a = node.attributes[i];
21554             //console.log(a);
21555             
21556             if (a.name.toLowerCase().substr(0,2)=='on')  {
21557                 node.removeAttribute(a.name);
21558                 continue;
21559             }
21560             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21561                 node.removeAttribute(a.name);
21562                 continue;
21563             }
21564             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21565                 cleanAttr(a.name,a.value); // fixme..
21566                 continue;
21567             }
21568             if (a.name == 'style') {
21569                 cleanStyle(a.name,a.value);
21570                 continue;
21571             }
21572             /// clean up MS crap..
21573             // tecnically this should be a list of valid class'es..
21574             
21575             
21576             if (a.name == 'class') {
21577                 if (a.value.match(/^Mso/)) {
21578                     node.className = '';
21579                 }
21580                 
21581                 if (a.value.match(/^body$/)) {
21582                     node.className = '';
21583                 }
21584                 continue;
21585             }
21586             
21587             // style cleanup!?
21588             // class cleanup?
21589             
21590         }
21591         
21592         
21593         this.cleanUpChildren(node);
21594         
21595         
21596     },
21597     
21598     /**
21599      * Clean up MS wordisms...
21600      */
21601     cleanWord : function(node)
21602     {
21603         
21604         
21605         if (!node) {
21606             this.cleanWord(this.doc.body);
21607             return;
21608         }
21609         if (node.nodeName == "#text") {
21610             // clean up silly Windows -- stuff?
21611             return; 
21612         }
21613         if (node.nodeName == "#comment") {
21614             node.parentNode.removeChild(node);
21615             // clean up silly Windows -- stuff?
21616             return; 
21617         }
21618         
21619         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21620             node.parentNode.removeChild(node);
21621             return;
21622         }
21623         
21624         // remove - but keep children..
21625         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
21626             while (node.childNodes.length) {
21627                 var cn = node.childNodes[0];
21628                 node.removeChild(cn);
21629                 node.parentNode.insertBefore(cn, node);
21630             }
21631             node.parentNode.removeChild(node);
21632             this.iterateChildren(node, this.cleanWord);
21633             return;
21634         }
21635         // clean styles
21636         if (node.className.length) {
21637             
21638             var cn = node.className.split(/\W+/);
21639             var cna = [];
21640             Roo.each(cn, function(cls) {
21641                 if (cls.match(/Mso[a-zA-Z]+/)) {
21642                     return;
21643                 }
21644                 cna.push(cls);
21645             });
21646             node.className = cna.length ? cna.join(' ') : '';
21647             if (!cna.length) {
21648                 node.removeAttribute("class");
21649             }
21650         }
21651         
21652         if (node.hasAttribute("lang")) {
21653             node.removeAttribute("lang");
21654         }
21655         
21656         if (node.hasAttribute("style")) {
21657             
21658             var styles = node.getAttribute("style").split(";");
21659             var nstyle = [];
21660             Roo.each(styles, function(s) {
21661                 if (!s.match(/:/)) {
21662                     return;
21663                 }
21664                 var kv = s.split(":");
21665                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21666                     return;
21667                 }
21668                 // what ever is left... we allow.
21669                 nstyle.push(s);
21670             });
21671             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21672             if (!nstyle.length) {
21673                 node.removeAttribute('style');
21674             }
21675         }
21676         this.iterateChildren(node, this.cleanWord);
21677         
21678         
21679         
21680     },
21681     /**
21682      * iterateChildren of a Node, calling fn each time, using this as the scole..
21683      * @param {DomNode} node node to iterate children of.
21684      * @param {Function} fn method of this class to call on each item.
21685      */
21686     iterateChildren : function(node, fn)
21687     {
21688         if (!node.childNodes.length) {
21689                 return;
21690         }
21691         for (var i = node.childNodes.length-1; i > -1 ; i--) {
21692            fn.call(this, node.childNodes[i])
21693         }
21694     },
21695     
21696     
21697     /**
21698      * cleanTableWidths.
21699      *
21700      * Quite often pasting from word etc.. results in tables with column and widths.
21701      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21702      *
21703      */
21704     cleanTableWidths : function(node)
21705     {
21706          
21707          
21708         if (!node) {
21709             this.cleanTableWidths(this.doc.body);
21710             return;
21711         }
21712         
21713         // ignore list...
21714         if (node.nodeName == "#text" || node.nodeName == "#comment") {
21715             return; 
21716         }
21717         Roo.log(node.tagName);
21718         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21719             this.iterateChildren(node, this.cleanTableWidths);
21720             return;
21721         }
21722         if (node.hasAttribute('width')) {
21723             node.removeAttribute('width');
21724         }
21725         
21726          
21727         if (node.hasAttribute("style")) {
21728             // pretty basic...
21729             
21730             var styles = node.getAttribute("style").split(";");
21731             var nstyle = [];
21732             Roo.each(styles, function(s) {
21733                 if (!s.match(/:/)) {
21734                     return;
21735                 }
21736                 var kv = s.split(":");
21737                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21738                     return;
21739                 }
21740                 // what ever is left... we allow.
21741                 nstyle.push(s);
21742             });
21743             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21744             if (!nstyle.length) {
21745                 node.removeAttribute('style');
21746             }
21747         }
21748         
21749         this.iterateChildren(node, this.cleanTableWidths);
21750         
21751         
21752     },
21753     
21754     
21755     
21756     
21757     domToHTML : function(currentElement, depth, nopadtext) {
21758         
21759         depth = depth || 0;
21760         nopadtext = nopadtext || false;
21761     
21762         if (!currentElement) {
21763             return this.domToHTML(this.doc.body);
21764         }
21765         
21766         //Roo.log(currentElement);
21767         var j;
21768         var allText = false;
21769         var nodeName = currentElement.nodeName;
21770         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21771         
21772         if  (nodeName == '#text') {
21773             
21774             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21775         }
21776         
21777         
21778         var ret = '';
21779         if (nodeName != 'BODY') {
21780              
21781             var i = 0;
21782             // Prints the node tagName, such as <A>, <IMG>, etc
21783             if (tagName) {
21784                 var attr = [];
21785                 for(i = 0; i < currentElement.attributes.length;i++) {
21786                     // quoting?
21787                     var aname = currentElement.attributes.item(i).name;
21788                     if (!currentElement.attributes.item(i).value.length) {
21789                         continue;
21790                     }
21791                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21792                 }
21793                 
21794                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21795             } 
21796             else {
21797                 
21798                 // eack
21799             }
21800         } else {
21801             tagName = false;
21802         }
21803         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21804             return ret;
21805         }
21806         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21807             nopadtext = true;
21808         }
21809         
21810         
21811         // Traverse the tree
21812         i = 0;
21813         var currentElementChild = currentElement.childNodes.item(i);
21814         var allText = true;
21815         var innerHTML  = '';
21816         lastnode = '';
21817         while (currentElementChild) {
21818             // Formatting code (indent the tree so it looks nice on the screen)
21819             var nopad = nopadtext;
21820             if (lastnode == 'SPAN') {
21821                 nopad  = true;
21822             }
21823             // text
21824             if  (currentElementChild.nodeName == '#text') {
21825                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21826                 toadd = nopadtext ? toadd : toadd.trim();
21827                 if (!nopad && toadd.length > 80) {
21828                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
21829                 }
21830                 innerHTML  += toadd;
21831                 
21832                 i++;
21833                 currentElementChild = currentElement.childNodes.item(i);
21834                 lastNode = '';
21835                 continue;
21836             }
21837             allText = false;
21838             
21839             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
21840                 
21841             // Recursively traverse the tree structure of the child node
21842             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
21843             lastnode = currentElementChild.nodeName;
21844             i++;
21845             currentElementChild=currentElement.childNodes.item(i);
21846         }
21847         
21848         ret += innerHTML;
21849         
21850         if (!allText) {
21851                 // The remaining code is mostly for formatting the tree
21852             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
21853         }
21854         
21855         
21856         if (tagName) {
21857             ret+= "</"+tagName+">";
21858         }
21859         return ret;
21860         
21861     },
21862         
21863     applyBlacklists : function()
21864     {
21865         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
21866         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
21867         
21868         this.white = [];
21869         this.black = [];
21870         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
21871             if (b.indexOf(tag) > -1) {
21872                 return;
21873             }
21874             this.white.push(tag);
21875             
21876         }, this);
21877         
21878         Roo.each(w, function(tag) {
21879             if (b.indexOf(tag) > -1) {
21880                 return;
21881             }
21882             if (this.white.indexOf(tag) > -1) {
21883                 return;
21884             }
21885             this.white.push(tag);
21886             
21887         }, this);
21888         
21889         
21890         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
21891             if (w.indexOf(tag) > -1) {
21892                 return;
21893             }
21894             this.black.push(tag);
21895             
21896         }, this);
21897         
21898         Roo.each(b, function(tag) {
21899             if (w.indexOf(tag) > -1) {
21900                 return;
21901             }
21902             if (this.black.indexOf(tag) > -1) {
21903                 return;
21904             }
21905             this.black.push(tag);
21906             
21907         }, this);
21908         
21909         
21910         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
21911         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
21912         
21913         this.cwhite = [];
21914         this.cblack = [];
21915         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
21916             if (b.indexOf(tag) > -1) {
21917                 return;
21918             }
21919             this.cwhite.push(tag);
21920             
21921         }, this);
21922         
21923         Roo.each(w, function(tag) {
21924             if (b.indexOf(tag) > -1) {
21925                 return;
21926             }
21927             if (this.cwhite.indexOf(tag) > -1) {
21928                 return;
21929             }
21930             this.cwhite.push(tag);
21931             
21932         }, this);
21933         
21934         
21935         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
21936             if (w.indexOf(tag) > -1) {
21937                 return;
21938             }
21939             this.cblack.push(tag);
21940             
21941         }, this);
21942         
21943         Roo.each(b, function(tag) {
21944             if (w.indexOf(tag) > -1) {
21945                 return;
21946             }
21947             if (this.cblack.indexOf(tag) > -1) {
21948                 return;
21949             }
21950             this.cblack.push(tag);
21951             
21952         }, this);
21953     },
21954     
21955     setStylesheets : function(stylesheets)
21956     {
21957         if(typeof(stylesheets) == 'string'){
21958             Roo.get(this.iframe.contentDocument.head).createChild({
21959                 tag : 'link',
21960                 rel : 'stylesheet',
21961                 type : 'text/css',
21962                 href : stylesheets
21963             });
21964             
21965             return;
21966         }
21967         var _this = this;
21968      
21969         Roo.each(stylesheets, function(s) {
21970             if(!s.length){
21971                 return;
21972             }
21973             
21974             Roo.get(_this.iframe.contentDocument.head).createChild({
21975                 tag : 'link',
21976                 rel : 'stylesheet',
21977                 type : 'text/css',
21978                 href : s
21979             });
21980         });
21981
21982         
21983     },
21984     
21985     removeStylesheets : function()
21986     {
21987         var _this = this;
21988         
21989         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
21990             s.remove();
21991         });
21992     },
21993     
21994     setStyle : function(style)
21995     {
21996         Roo.get(this.iframe.contentDocument.head).createChild({
21997             tag : 'style',
21998             type : 'text/css',
21999             html : style
22000         });
22001
22002         return;
22003     }
22004     
22005     // hide stuff that is not compatible
22006     /**
22007      * @event blur
22008      * @hide
22009      */
22010     /**
22011      * @event change
22012      * @hide
22013      */
22014     /**
22015      * @event focus
22016      * @hide
22017      */
22018     /**
22019      * @event specialkey
22020      * @hide
22021      */
22022     /**
22023      * @cfg {String} fieldClass @hide
22024      */
22025     /**
22026      * @cfg {String} focusClass @hide
22027      */
22028     /**
22029      * @cfg {String} autoCreate @hide
22030      */
22031     /**
22032      * @cfg {String} inputType @hide
22033      */
22034     /**
22035      * @cfg {String} invalidClass @hide
22036      */
22037     /**
22038      * @cfg {String} invalidText @hide
22039      */
22040     /**
22041      * @cfg {String} msgFx @hide
22042      */
22043     /**
22044      * @cfg {String} validateOnBlur @hide
22045      */
22046 });
22047
22048 Roo.HtmlEditorCore.white = [
22049         'area', 'br', 'img', 'input', 'hr', 'wbr',
22050         
22051        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
22052        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
22053        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
22054        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
22055        'table',   'ul',         'xmp', 
22056        
22057        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
22058       'thead',   'tr', 
22059      
22060       'dir', 'menu', 'ol', 'ul', 'dl',
22061        
22062       'embed',  'object'
22063 ];
22064
22065
22066 Roo.HtmlEditorCore.black = [
22067     //    'embed',  'object', // enable - backend responsiblity to clean thiese
22068         'applet', // 
22069         'base',   'basefont', 'bgsound', 'blink',  'body', 
22070         'frame',  'frameset', 'head',    'html',   'ilayer', 
22071         'iframe', 'layer',  'link',     'meta',    'object',   
22072         'script', 'style' ,'title',  'xml' // clean later..
22073 ];
22074 Roo.HtmlEditorCore.clean = [
22075     'script', 'style', 'title', 'xml'
22076 ];
22077 Roo.HtmlEditorCore.remove = [
22078     'font'
22079 ];
22080 // attributes..
22081
22082 Roo.HtmlEditorCore.ablack = [
22083     'on'
22084 ];
22085     
22086 Roo.HtmlEditorCore.aclean = [ 
22087     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22088 ];
22089
22090 // protocols..
22091 Roo.HtmlEditorCore.pwhite= [
22092         'http',  'https',  'mailto'
22093 ];
22094
22095 // white listed style attributes.
22096 Roo.HtmlEditorCore.cwhite= [
22097       //  'text-align', /// default is to allow most things..
22098       
22099          
22100 //        'font-size'//??
22101 ];
22102
22103 // black listed style attributes.
22104 Roo.HtmlEditorCore.cblack= [
22105       //  'font-size' -- this can be set by the project 
22106 ];
22107
22108
22109 Roo.HtmlEditorCore.swapCodes   =[ 
22110     [    8211, "--" ], 
22111     [    8212, "--" ], 
22112     [    8216,  "'" ],  
22113     [    8217, "'" ],  
22114     [    8220, '"' ],  
22115     [    8221, '"' ],  
22116     [    8226, "*" ],  
22117     [    8230, "..." ]
22118 ]; 
22119
22120     //<script type="text/javascript">
22121
22122 /*
22123  * Ext JS Library 1.1.1
22124  * Copyright(c) 2006-2007, Ext JS, LLC.
22125  * Licence LGPL
22126  * 
22127  */
22128  
22129  
22130 Roo.form.HtmlEditor = function(config){
22131     
22132     
22133     
22134     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
22135     
22136     if (!this.toolbars) {
22137         this.toolbars = [];
22138     }
22139     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22140     
22141     
22142 };
22143
22144 /**
22145  * @class Roo.form.HtmlEditor
22146  * @extends Roo.form.Field
22147  * Provides a lightweight HTML Editor component.
22148  *
22149  * This has been tested on Fireforx / Chrome.. IE may not be so great..
22150  * 
22151  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
22152  * supported by this editor.</b><br/><br/>
22153  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
22154  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22155  */
22156 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
22157     /**
22158      * @cfg {Boolean} clearUp
22159      */
22160     clearUp : true,
22161       /**
22162      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22163      */
22164     toolbars : false,
22165    
22166      /**
22167      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22168      *                        Roo.resizable.
22169      */
22170     resizable : false,
22171      /**
22172      * @cfg {Number} height (in pixels)
22173      */   
22174     height: 300,
22175    /**
22176      * @cfg {Number} width (in pixels)
22177      */   
22178     width: 500,
22179     
22180     /**
22181      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22182      * 
22183      */
22184     stylesheets: false,
22185     
22186     
22187      /**
22188      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
22189      * 
22190      */
22191     cblack: false,
22192     /**
22193      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
22194      * 
22195      */
22196     cwhite: false,
22197     
22198      /**
22199      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
22200      * 
22201      */
22202     black: false,
22203     /**
22204      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
22205      * 
22206      */
22207     white: false,
22208     
22209     // id of frame..
22210     frameId: false,
22211     
22212     // private properties
22213     validationEvent : false,
22214     deferHeight: true,
22215     initialized : false,
22216     activated : false,
22217     
22218     onFocus : Roo.emptyFn,
22219     iframePad:3,
22220     hideMode:'offsets',
22221     
22222     actionMode : 'container', // defaults to hiding it...
22223     
22224     defaultAutoCreate : { // modified by initCompnoent..
22225         tag: "textarea",
22226         style:"width:500px;height:300px;",
22227         autocomplete: "new-password"
22228     },
22229
22230     // private
22231     initComponent : function(){
22232         this.addEvents({
22233             /**
22234              * @event initialize
22235              * Fires when the editor is fully initialized (including the iframe)
22236              * @param {HtmlEditor} this
22237              */
22238             initialize: true,
22239             /**
22240              * @event activate
22241              * Fires when the editor is first receives the focus. Any insertion must wait
22242              * until after this event.
22243              * @param {HtmlEditor} this
22244              */
22245             activate: true,
22246              /**
22247              * @event beforesync
22248              * Fires before the textarea is updated with content from the editor iframe. Return false
22249              * to cancel the sync.
22250              * @param {HtmlEditor} this
22251              * @param {String} html
22252              */
22253             beforesync: true,
22254              /**
22255              * @event beforepush
22256              * Fires before the iframe editor is updated with content from the textarea. Return false
22257              * to cancel the push.
22258              * @param {HtmlEditor} this
22259              * @param {String} html
22260              */
22261             beforepush: true,
22262              /**
22263              * @event sync
22264              * Fires when the textarea is updated with content from the editor iframe.
22265              * @param {HtmlEditor} this
22266              * @param {String} html
22267              */
22268             sync: true,
22269              /**
22270              * @event push
22271              * Fires when the iframe editor is updated with content from the textarea.
22272              * @param {HtmlEditor} this
22273              * @param {String} html
22274              */
22275             push: true,
22276              /**
22277              * @event editmodechange
22278              * Fires when the editor switches edit modes
22279              * @param {HtmlEditor} this
22280              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22281              */
22282             editmodechange: true,
22283             /**
22284              * @event editorevent
22285              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22286              * @param {HtmlEditor} this
22287              */
22288             editorevent: true,
22289             /**
22290              * @event firstfocus
22291              * Fires when on first focus - needed by toolbars..
22292              * @param {HtmlEditor} this
22293              */
22294             firstfocus: true,
22295             /**
22296              * @event autosave
22297              * Auto save the htmlEditor value as a file into Events
22298              * @param {HtmlEditor} this
22299              */
22300             autosave: true,
22301             /**
22302              * @event savedpreview
22303              * preview the saved version of htmlEditor
22304              * @param {HtmlEditor} this
22305              */
22306             savedpreview: true,
22307             
22308             /**
22309             * @event stylesheetsclick
22310             * Fires when press the Sytlesheets button
22311             * @param {Roo.HtmlEditorCore} this
22312             */
22313             stylesheetsclick: true
22314         });
22315         this.defaultAutoCreate =  {
22316             tag: "textarea",
22317             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
22318             autocomplete: "new-password"
22319         };
22320     },
22321
22322     /**
22323      * Protected method that will not generally be called directly. It
22324      * is called when the editor creates its toolbar. Override this method if you need to
22325      * add custom toolbar buttons.
22326      * @param {HtmlEditor} editor
22327      */
22328     createToolbar : function(editor){
22329         Roo.log("create toolbars");
22330         if (!editor.toolbars || !editor.toolbars.length) {
22331             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
22332         }
22333         
22334         for (var i =0 ; i < editor.toolbars.length;i++) {
22335             editor.toolbars[i] = Roo.factory(
22336                     typeof(editor.toolbars[i]) == 'string' ?
22337                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
22338                 Roo.form.HtmlEditor);
22339             editor.toolbars[i].init(editor);
22340         }
22341          
22342         
22343     },
22344
22345      
22346     // private
22347     onRender : function(ct, position)
22348     {
22349         var _t = this;
22350         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
22351         
22352         this.wrap = this.el.wrap({
22353             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22354         });
22355         
22356         this.editorcore.onRender(ct, position);
22357          
22358         if (this.resizable) {
22359             this.resizeEl = new Roo.Resizable(this.wrap, {
22360                 pinned : true,
22361                 wrap: true,
22362                 dynamic : true,
22363                 minHeight : this.height,
22364                 height: this.height,
22365                 handles : this.resizable,
22366                 width: this.width,
22367                 listeners : {
22368                     resize : function(r, w, h) {
22369                         _t.onResize(w,h); // -something
22370                     }
22371                 }
22372             });
22373             
22374         }
22375         this.createToolbar(this);
22376        
22377         
22378         if(!this.width){
22379             this.setSize(this.wrap.getSize());
22380         }
22381         if (this.resizeEl) {
22382             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22383             // should trigger onReize..
22384         }
22385         
22386         this.keyNav = new Roo.KeyNav(this.el, {
22387             
22388             "tab" : function(e){
22389                 e.preventDefault();
22390                 
22391                 var value = this.getValue();
22392                 
22393                 var start = this.el.dom.selectionStart;
22394                 var end = this.el.dom.selectionEnd;
22395                 
22396                 if(!e.shiftKey){
22397                     
22398                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
22399                     this.el.dom.setSelectionRange(end + 1, end + 1);
22400                     return;
22401                 }
22402                 
22403                 var f = value.substring(0, start).split("\t");
22404                 
22405                 if(f.pop().length != 0){
22406                     return;
22407                 }
22408                 
22409                 this.setValue(f.join("\t") + value.substring(end));
22410                 this.el.dom.setSelectionRange(start - 1, start - 1);
22411                 
22412             },
22413             
22414             "home" : function(e){
22415                 e.preventDefault();
22416                 
22417                 var curr = this.el.dom.selectionStart;
22418                 var lines = this.getValue().split("\n");
22419                 
22420                 if(!lines.length){
22421                     return;
22422                 }
22423                 
22424                 if(e.ctrlKey){
22425                     this.el.dom.setSelectionRange(0, 0);
22426                     return;
22427                 }
22428                 
22429                 var pos = 0;
22430                 
22431                 for (var i = 0; i < lines.length;i++) {
22432                     pos += lines[i].length;
22433                     
22434                     if(i != 0){
22435                         pos += 1;
22436                     }
22437                     
22438                     if(pos < curr){
22439                         continue;
22440                     }
22441                     
22442                     pos -= lines[i].length;
22443                     
22444                     break;
22445                 }
22446                 
22447                 if(!e.shiftKey){
22448                     this.el.dom.setSelectionRange(pos, pos);
22449                     return;
22450                 }
22451                 
22452                 this.el.dom.selectionStart = pos;
22453                 this.el.dom.selectionEnd = curr;
22454             },
22455             
22456             "end" : function(e){
22457                 e.preventDefault();
22458                 
22459                 var curr = this.el.dom.selectionStart;
22460                 var lines = this.getValue().split("\n");
22461                 
22462                 if(!lines.length){
22463                     return;
22464                 }
22465                 
22466                 if(e.ctrlKey){
22467                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
22468                     return;
22469                 }
22470                 
22471                 var pos = 0;
22472                 
22473                 for (var i = 0; i < lines.length;i++) {
22474                     
22475                     pos += lines[i].length;
22476                     
22477                     if(i != 0){
22478                         pos += 1;
22479                     }
22480                     
22481                     if(pos < curr){
22482                         continue;
22483                     }
22484                     
22485                     break;
22486                 }
22487                 
22488                 if(!e.shiftKey){
22489                     this.el.dom.setSelectionRange(pos, pos);
22490                     return;
22491                 }
22492                 
22493                 this.el.dom.selectionStart = curr;
22494                 this.el.dom.selectionEnd = pos;
22495             },
22496
22497             scope : this,
22498
22499             doRelay : function(foo, bar, hname){
22500                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
22501             },
22502
22503             forceKeyDown: true
22504         });
22505         
22506 //        if(this.autosave && this.w){
22507 //            this.autoSaveFn = setInterval(this.autosave, 1000);
22508 //        }
22509     },
22510
22511     // private
22512     onResize : function(w, h)
22513     {
22514         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
22515         var ew = false;
22516         var eh = false;
22517         
22518         if(this.el ){
22519             if(typeof w == 'number'){
22520                 var aw = w - this.wrap.getFrameWidth('lr');
22521                 this.el.setWidth(this.adjustWidth('textarea', aw));
22522                 ew = aw;
22523             }
22524             if(typeof h == 'number'){
22525                 var tbh = 0;
22526                 for (var i =0; i < this.toolbars.length;i++) {
22527                     // fixme - ask toolbars for heights?
22528                     tbh += this.toolbars[i].tb.el.getHeight();
22529                     if (this.toolbars[i].footer) {
22530                         tbh += this.toolbars[i].footer.el.getHeight();
22531                     }
22532                 }
22533                 
22534                 
22535                 
22536                 
22537                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
22538                 ah -= 5; // knock a few pixes off for look..
22539 //                Roo.log(ah);
22540                 this.el.setHeight(this.adjustWidth('textarea', ah));
22541                 var eh = ah;
22542             }
22543         }
22544         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
22545         this.editorcore.onResize(ew,eh);
22546         
22547     },
22548
22549     /**
22550      * Toggles the editor between standard and source edit mode.
22551      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22552      */
22553     toggleSourceEdit : function(sourceEditMode)
22554     {
22555         this.editorcore.toggleSourceEdit(sourceEditMode);
22556         
22557         if(this.editorcore.sourceEditMode){
22558             Roo.log('editor - showing textarea');
22559             
22560 //            Roo.log('in');
22561 //            Roo.log(this.syncValue());
22562             this.editorcore.syncValue();
22563             this.el.removeClass('x-hidden');
22564             this.el.dom.removeAttribute('tabIndex');
22565             this.el.focus();
22566             
22567             for (var i = 0; i < this.toolbars.length; i++) {
22568                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
22569                     this.toolbars[i].tb.hide();
22570                     this.toolbars[i].footer.hide();
22571                 }
22572             }
22573             
22574         }else{
22575             Roo.log('editor - hiding textarea');
22576 //            Roo.log('out')
22577 //            Roo.log(this.pushValue()); 
22578             this.editorcore.pushValue();
22579             
22580             this.el.addClass('x-hidden');
22581             this.el.dom.setAttribute('tabIndex', -1);
22582             
22583             for (var i = 0; i < this.toolbars.length; i++) {
22584                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
22585                     this.toolbars[i].tb.show();
22586                     this.toolbars[i].footer.show();
22587                 }
22588             }
22589             
22590             //this.deferFocus();
22591         }
22592         
22593         this.setSize(this.wrap.getSize());
22594         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
22595         
22596         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
22597     },
22598  
22599     // private (for BoxComponent)
22600     adjustSize : Roo.BoxComponent.prototype.adjustSize,
22601
22602     // private (for BoxComponent)
22603     getResizeEl : function(){
22604         return this.wrap;
22605     },
22606
22607     // private (for BoxComponent)
22608     getPositionEl : function(){
22609         return this.wrap;
22610     },
22611
22612     // private
22613     initEvents : function(){
22614         this.originalValue = this.getValue();
22615     },
22616
22617     /**
22618      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22619      * @method
22620      */
22621     markInvalid : Roo.emptyFn,
22622     /**
22623      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22624      * @method
22625      */
22626     clearInvalid : Roo.emptyFn,
22627
22628     setValue : function(v){
22629         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
22630         this.editorcore.pushValue();
22631     },
22632
22633      
22634     // private
22635     deferFocus : function(){
22636         this.focus.defer(10, this);
22637     },
22638
22639     // doc'ed in Field
22640     focus : function(){
22641         this.editorcore.focus();
22642         
22643     },
22644       
22645
22646     // private
22647     onDestroy : function(){
22648         
22649         
22650         
22651         if(this.rendered){
22652             
22653             for (var i =0; i < this.toolbars.length;i++) {
22654                 // fixme - ask toolbars for heights?
22655                 this.toolbars[i].onDestroy();
22656             }
22657             
22658             this.wrap.dom.innerHTML = '';
22659             this.wrap.remove();
22660         }
22661     },
22662
22663     // private
22664     onFirstFocus : function(){
22665         //Roo.log("onFirstFocus");
22666         this.editorcore.onFirstFocus();
22667          for (var i =0; i < this.toolbars.length;i++) {
22668             this.toolbars[i].onFirstFocus();
22669         }
22670         
22671     },
22672     
22673     // private
22674     syncValue : function()
22675     {
22676         this.editorcore.syncValue();
22677     },
22678     
22679     pushValue : function()
22680     {
22681         this.editorcore.pushValue();
22682     },
22683     
22684     setStylesheets : function(stylesheets)
22685     {
22686         this.editorcore.setStylesheets(stylesheets);
22687     },
22688     
22689     removeStylesheets : function()
22690     {
22691         this.editorcore.removeStylesheets();
22692     }
22693      
22694     
22695     // hide stuff that is not compatible
22696     /**
22697      * @event blur
22698      * @hide
22699      */
22700     /**
22701      * @event change
22702      * @hide
22703      */
22704     /**
22705      * @event focus
22706      * @hide
22707      */
22708     /**
22709      * @event specialkey
22710      * @hide
22711      */
22712     /**
22713      * @cfg {String} fieldClass @hide
22714      */
22715     /**
22716      * @cfg {String} focusClass @hide
22717      */
22718     /**
22719      * @cfg {String} autoCreate @hide
22720      */
22721     /**
22722      * @cfg {String} inputType @hide
22723      */
22724     /**
22725      * @cfg {String} invalidClass @hide
22726      */
22727     /**
22728      * @cfg {String} invalidText @hide
22729      */
22730     /**
22731      * @cfg {String} msgFx @hide
22732      */
22733     /**
22734      * @cfg {String} validateOnBlur @hide
22735      */
22736 });
22737  
22738     // <script type="text/javascript">
22739 /*
22740  * Based on
22741  * Ext JS Library 1.1.1
22742  * Copyright(c) 2006-2007, Ext JS, LLC.
22743  *  
22744  
22745  */
22746
22747 /**
22748  * @class Roo.form.HtmlEditorToolbar1
22749  * Basic Toolbar
22750  * 
22751  * Usage:
22752  *
22753  new Roo.form.HtmlEditor({
22754     ....
22755     toolbars : [
22756         new Roo.form.HtmlEditorToolbar1({
22757             disable : { fonts: 1 , format: 1, ..., ... , ...],
22758             btns : [ .... ]
22759         })
22760     }
22761      
22762  * 
22763  * @cfg {Object} disable List of elements to disable..
22764  * @cfg {Array} btns List of additional buttons.
22765  * 
22766  * 
22767  * NEEDS Extra CSS? 
22768  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22769  */
22770  
22771 Roo.form.HtmlEditor.ToolbarStandard = function(config)
22772 {
22773     
22774     Roo.apply(this, config);
22775     
22776     // default disabled, based on 'good practice'..
22777     this.disable = this.disable || {};
22778     Roo.applyIf(this.disable, {
22779         fontSize : true,
22780         colors : true,
22781         specialElements : true
22782     });
22783     
22784     
22785     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22786     // dont call parent... till later.
22787 }
22788
22789 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
22790     
22791     tb: false,
22792     
22793     rendered: false,
22794     
22795     editor : false,
22796     editorcore : false,
22797     /**
22798      * @cfg {Object} disable  List of toolbar elements to disable
22799          
22800      */
22801     disable : false,
22802     
22803     
22804      /**
22805      * @cfg {String} createLinkText The default text for the create link prompt
22806      */
22807     createLinkText : 'Please enter the URL for the link:',
22808     /**
22809      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
22810      */
22811     defaultLinkValue : 'http:/'+'/',
22812    
22813     
22814       /**
22815      * @cfg {Array} fontFamilies An array of available font families
22816      */
22817     fontFamilies : [
22818         'Arial',
22819         'Courier New',
22820         'Tahoma',
22821         'Times New Roman',
22822         'Verdana'
22823     ],
22824     
22825     specialChars : [
22826            "&#169;",
22827           "&#174;",     
22828           "&#8482;",    
22829           "&#163;" ,    
22830          // "&#8212;",    
22831           "&#8230;",    
22832           "&#247;" ,    
22833         //  "&#225;" ,     ?? a acute?
22834            "&#8364;"    , //Euro
22835        //   "&#8220;"    ,
22836         //  "&#8221;"    ,
22837         //  "&#8226;"    ,
22838           "&#176;"  //   , // degrees
22839
22840          // "&#233;"     , // e ecute
22841          // "&#250;"     , // u ecute?
22842     ],
22843     
22844     specialElements : [
22845         {
22846             text: "Insert Table",
22847             xtype: 'MenuItem',
22848             xns : Roo.Menu,
22849             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
22850                 
22851         },
22852         {    
22853             text: "Insert Image",
22854             xtype: 'MenuItem',
22855             xns : Roo.Menu,
22856             ihtml : '<img src="about:blank"/>'
22857             
22858         }
22859         
22860          
22861     ],
22862     
22863     
22864     inputElements : [ 
22865             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
22866             "input:submit", "input:button", "select", "textarea", "label" ],
22867     formats : [
22868         ["p"] ,  
22869         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
22870         ["pre"],[ "code"], 
22871         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
22872         ['div'],['span']
22873     ],
22874     
22875     cleanStyles : [
22876         "font-size"
22877     ],
22878      /**
22879      * @cfg {String} defaultFont default font to use.
22880      */
22881     defaultFont: 'tahoma',
22882    
22883     fontSelect : false,
22884     
22885     
22886     formatCombo : false,
22887     
22888     init : function(editor)
22889     {
22890         this.editor = editor;
22891         this.editorcore = editor.editorcore ? editor.editorcore : editor;
22892         var editorcore = this.editorcore;
22893         
22894         var _t = this;
22895         
22896         var fid = editorcore.frameId;
22897         var etb = this;
22898         function btn(id, toggle, handler){
22899             var xid = fid + '-'+ id ;
22900             return {
22901                 id : xid,
22902                 cmd : id,
22903                 cls : 'x-btn-icon x-edit-'+id,
22904                 enableToggle:toggle !== false,
22905                 scope: _t, // was editor...
22906                 handler:handler||_t.relayBtnCmd,
22907                 clickEvent:'mousedown',
22908                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
22909                 tabIndex:-1
22910             };
22911         }
22912         
22913         
22914         
22915         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
22916         this.tb = tb;
22917          // stop form submits
22918         tb.el.on('click', function(e){
22919             e.preventDefault(); // what does this do?
22920         });
22921
22922         if(!this.disable.font) { // && !Roo.isSafari){
22923             /* why no safari for fonts 
22924             editor.fontSelect = tb.el.createChild({
22925                 tag:'select',
22926                 tabIndex: -1,
22927                 cls:'x-font-select',
22928                 html: this.createFontOptions()
22929             });
22930             
22931             editor.fontSelect.on('change', function(){
22932                 var font = editor.fontSelect.dom.value;
22933                 editor.relayCmd('fontname', font);
22934                 editor.deferFocus();
22935             }, editor);
22936             
22937             tb.add(
22938                 editor.fontSelect.dom,
22939                 '-'
22940             );
22941             */
22942             
22943         };
22944         if(!this.disable.formats){
22945             this.formatCombo = new Roo.form.ComboBox({
22946                 store: new Roo.data.SimpleStore({
22947                     id : 'tag',
22948                     fields: ['tag'],
22949                     data : this.formats // from states.js
22950                 }),
22951                 blockFocus : true,
22952                 name : '',
22953                 //autoCreate : {tag: "div",  size: "20"},
22954                 displayField:'tag',
22955                 typeAhead: false,
22956                 mode: 'local',
22957                 editable : false,
22958                 triggerAction: 'all',
22959                 emptyText:'Add tag',
22960                 selectOnFocus:true,
22961                 width:135,
22962                 listeners : {
22963                     'select': function(c, r, i) {
22964                         editorcore.insertTag(r.get('tag'));
22965                         editor.focus();
22966                     }
22967                 }
22968
22969             });
22970             tb.addField(this.formatCombo);
22971             
22972         }
22973         
22974         if(!this.disable.format){
22975             tb.add(
22976                 btn('bold'),
22977                 btn('italic'),
22978                 btn('underline'),
22979                 btn('strikethrough')
22980             );
22981         };
22982         if(!this.disable.fontSize){
22983             tb.add(
22984                 '-',
22985                 
22986                 
22987                 btn('increasefontsize', false, editorcore.adjustFont),
22988                 btn('decreasefontsize', false, editorcore.adjustFont)
22989             );
22990         };
22991         
22992         
22993         if(!this.disable.colors){
22994             tb.add(
22995                 '-', {
22996                     id:editorcore.frameId +'-forecolor',
22997                     cls:'x-btn-icon x-edit-forecolor',
22998                     clickEvent:'mousedown',
22999                     tooltip: this.buttonTips['forecolor'] || undefined,
23000                     tabIndex:-1,
23001                     menu : new Roo.menu.ColorMenu({
23002                         allowReselect: true,
23003                         focus: Roo.emptyFn,
23004                         value:'000000',
23005                         plain:true,
23006                         selectHandler: function(cp, color){
23007                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
23008                             editor.deferFocus();
23009                         },
23010                         scope: editorcore,
23011                         clickEvent:'mousedown'
23012                     })
23013                 }, {
23014                     id:editorcore.frameId +'backcolor',
23015                     cls:'x-btn-icon x-edit-backcolor',
23016                     clickEvent:'mousedown',
23017                     tooltip: this.buttonTips['backcolor'] || undefined,
23018                     tabIndex:-1,
23019                     menu : new Roo.menu.ColorMenu({
23020                         focus: Roo.emptyFn,
23021                         value:'FFFFFF',
23022                         plain:true,
23023                         allowReselect: true,
23024                         selectHandler: function(cp, color){
23025                             if(Roo.isGecko){
23026                                 editorcore.execCmd('useCSS', false);
23027                                 editorcore.execCmd('hilitecolor', color);
23028                                 editorcore.execCmd('useCSS', true);
23029                                 editor.deferFocus();
23030                             }else{
23031                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
23032                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
23033                                 editor.deferFocus();
23034                             }
23035                         },
23036                         scope:editorcore,
23037                         clickEvent:'mousedown'
23038                     })
23039                 }
23040             );
23041         };
23042         // now add all the items...
23043         
23044
23045         if(!this.disable.alignments){
23046             tb.add(
23047                 '-',
23048                 btn('justifyleft'),
23049                 btn('justifycenter'),
23050                 btn('justifyright')
23051             );
23052         };
23053
23054         //if(!Roo.isSafari){
23055             if(!this.disable.links){
23056                 tb.add(
23057                     '-',
23058                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
23059                 );
23060             };
23061
23062             if(!this.disable.lists){
23063                 tb.add(
23064                     '-',
23065                     btn('insertorderedlist'),
23066                     btn('insertunorderedlist')
23067                 );
23068             }
23069             if(!this.disable.sourceEdit){
23070                 tb.add(
23071                     '-',
23072                     btn('sourceedit', true, function(btn){
23073                         this.toggleSourceEdit(btn.pressed);
23074                     })
23075                 );
23076             }
23077         //}
23078         
23079         var smenu = { };
23080         // special menu.. - needs to be tidied up..
23081         if (!this.disable.special) {
23082             smenu = {
23083                 text: "&#169;",
23084                 cls: 'x-edit-none',
23085                 
23086                 menu : {
23087                     items : []
23088                 }
23089             };
23090             for (var i =0; i < this.specialChars.length; i++) {
23091                 smenu.menu.items.push({
23092                     
23093                     html: this.specialChars[i],
23094                     handler: function(a,b) {
23095                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
23096                         //editor.insertAtCursor(a.html);
23097                         
23098                     },
23099                     tabIndex:-1
23100                 });
23101             }
23102             
23103             
23104             tb.add(smenu);
23105             
23106             
23107         }
23108         
23109         var cmenu = { };
23110         if (!this.disable.cleanStyles) {
23111             cmenu = {
23112                 cls: 'x-btn-icon x-btn-clear',
23113                 
23114                 menu : {
23115                     items : []
23116                 }
23117             };
23118             for (var i =0; i < this.cleanStyles.length; i++) {
23119                 cmenu.menu.items.push({
23120                     actiontype : this.cleanStyles[i],
23121                     html: 'Remove ' + this.cleanStyles[i],
23122                     handler: function(a,b) {
23123 //                        Roo.log(a);
23124 //                        Roo.log(b);
23125                         var c = Roo.get(editorcore.doc.body);
23126                         c.select('[style]').each(function(s) {
23127                             s.dom.style.removeProperty(a.actiontype);
23128                         });
23129                         editorcore.syncValue();
23130                     },
23131                     tabIndex:-1
23132                 });
23133             }
23134              cmenu.menu.items.push({
23135                 actiontype : 'tablewidths',
23136                 html: 'Remove Table Widths',
23137                 handler: function(a,b) {
23138                     editorcore.cleanTableWidths();
23139                     editorcore.syncValue();
23140                 },
23141                 tabIndex:-1
23142             });
23143             cmenu.menu.items.push({
23144                 actiontype : 'word',
23145                 html: 'Remove MS Word Formating',
23146                 handler: function(a,b) {
23147                     editorcore.cleanWord();
23148                     editorcore.syncValue();
23149                 },
23150                 tabIndex:-1
23151             });
23152             
23153             cmenu.menu.items.push({
23154                 actiontype : 'all',
23155                 html: 'Remove All Styles',
23156                 handler: function(a,b) {
23157                     
23158                     var c = Roo.get(editorcore.doc.body);
23159                     c.select('[style]').each(function(s) {
23160                         s.dom.removeAttribute('style');
23161                     });
23162                     editorcore.syncValue();
23163                 },
23164                 tabIndex:-1
23165             });
23166             
23167             cmenu.menu.items.push({
23168                 actiontype : 'all',
23169                 html: 'Remove All CSS Classes',
23170                 handler: function(a,b) {
23171                     
23172                     var c = Roo.get(editorcore.doc.body);
23173                     c.select('[class]').each(function(s) {
23174                         s.dom.className = '';
23175                     });
23176                     editorcore.syncValue();
23177                 },
23178                 tabIndex:-1
23179             });
23180             
23181              cmenu.menu.items.push({
23182                 actiontype : 'tidy',
23183                 html: 'Tidy HTML Source',
23184                 handler: function(a,b) {
23185                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
23186                     editorcore.syncValue();
23187                 },
23188                 tabIndex:-1
23189             });
23190             
23191             
23192             tb.add(cmenu);
23193         }
23194          
23195         if (!this.disable.specialElements) {
23196             var semenu = {
23197                 text: "Other;",
23198                 cls: 'x-edit-none',
23199                 menu : {
23200                     items : []
23201                 }
23202             };
23203             for (var i =0; i < this.specialElements.length; i++) {
23204                 semenu.menu.items.push(
23205                     Roo.apply({ 
23206                         handler: function(a,b) {
23207                             editor.insertAtCursor(this.ihtml);
23208                         }
23209                     }, this.specialElements[i])
23210                 );
23211                     
23212             }
23213             
23214             tb.add(semenu);
23215             
23216             
23217         }
23218          
23219         
23220         if (this.btns) {
23221             for(var i =0; i< this.btns.length;i++) {
23222                 var b = Roo.factory(this.btns[i],Roo.form);
23223                 b.cls =  'x-edit-none';
23224                 
23225                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
23226                     b.cls += ' x-init-enable';
23227                 }
23228                 
23229                 b.scope = editorcore;
23230                 tb.add(b);
23231             }
23232         
23233         }
23234         
23235         
23236         
23237         // disable everything...
23238         
23239         this.tb.items.each(function(item){
23240             
23241            if(
23242                 item.id != editorcore.frameId+ '-sourceedit' && 
23243                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
23244             ){
23245                 
23246                 item.disable();
23247             }
23248         });
23249         this.rendered = true;
23250         
23251         // the all the btns;
23252         editor.on('editorevent', this.updateToolbar, this);
23253         // other toolbars need to implement this..
23254         //editor.on('editmodechange', this.updateToolbar, this);
23255     },
23256     
23257     
23258     relayBtnCmd : function(btn) {
23259         this.editorcore.relayCmd(btn.cmd);
23260     },
23261     // private used internally
23262     createLink : function(){
23263         Roo.log("create link?");
23264         var url = prompt(this.createLinkText, this.defaultLinkValue);
23265         if(url && url != 'http:/'+'/'){
23266             this.editorcore.relayCmd('createlink', url);
23267         }
23268     },
23269
23270     
23271     /**
23272      * Protected method that will not generally be called directly. It triggers
23273      * a toolbar update by reading the markup state of the current selection in the editor.
23274      */
23275     updateToolbar: function(){
23276
23277         if(!this.editorcore.activated){
23278             this.editor.onFirstFocus();
23279             return;
23280         }
23281
23282         var btns = this.tb.items.map, 
23283             doc = this.editorcore.doc,
23284             frameId = this.editorcore.frameId;
23285
23286         if(!this.disable.font && !Roo.isSafari){
23287             /*
23288             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
23289             if(name != this.fontSelect.dom.value){
23290                 this.fontSelect.dom.value = name;
23291             }
23292             */
23293         }
23294         if(!this.disable.format){
23295             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
23296             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
23297             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
23298             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
23299         }
23300         if(!this.disable.alignments){
23301             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
23302             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
23303             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
23304         }
23305         if(!Roo.isSafari && !this.disable.lists){
23306             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
23307             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
23308         }
23309         
23310         var ans = this.editorcore.getAllAncestors();
23311         if (this.formatCombo) {
23312             
23313             
23314             var store = this.formatCombo.store;
23315             this.formatCombo.setValue("");
23316             for (var i =0; i < ans.length;i++) {
23317                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23318                     // select it..
23319                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23320                     break;
23321                 }
23322             }
23323         }
23324         
23325         
23326         
23327         // hides menus... - so this cant be on a menu...
23328         Roo.menu.MenuMgr.hideAll();
23329
23330         //this.editorsyncValue();
23331     },
23332    
23333     
23334     createFontOptions : function(){
23335         var buf = [], fs = this.fontFamilies, ff, lc;
23336         
23337         
23338         
23339         for(var i = 0, len = fs.length; i< len; i++){
23340             ff = fs[i];
23341             lc = ff.toLowerCase();
23342             buf.push(
23343                 '<option value="',lc,'" style="font-family:',ff,';"',
23344                     (this.defaultFont == lc ? ' selected="true">' : '>'),
23345                     ff,
23346                 '</option>'
23347             );
23348         }
23349         return buf.join('');
23350     },
23351     
23352     toggleSourceEdit : function(sourceEditMode){
23353         
23354         Roo.log("toolbar toogle");
23355         if(sourceEditMode === undefined){
23356             sourceEditMode = !this.sourceEditMode;
23357         }
23358         this.sourceEditMode = sourceEditMode === true;
23359         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
23360         // just toggle the button?
23361         if(btn.pressed !== this.sourceEditMode){
23362             btn.toggle(this.sourceEditMode);
23363             return;
23364         }
23365         
23366         if(sourceEditMode){
23367             Roo.log("disabling buttons");
23368             this.tb.items.each(function(item){
23369                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
23370                     item.disable();
23371                 }
23372             });
23373           
23374         }else{
23375             Roo.log("enabling buttons");
23376             if(this.editorcore.initialized){
23377                 this.tb.items.each(function(item){
23378                     item.enable();
23379                 });
23380             }
23381             
23382         }
23383         Roo.log("calling toggole on editor");
23384         // tell the editor that it's been pressed..
23385         this.editor.toggleSourceEdit(sourceEditMode);
23386        
23387     },
23388      /**
23389      * Object collection of toolbar tooltips for the buttons in the editor. The key
23390      * is the command id associated with that button and the value is a valid QuickTips object.
23391      * For example:
23392 <pre><code>
23393 {
23394     bold : {
23395         title: 'Bold (Ctrl+B)',
23396         text: 'Make the selected text bold.',
23397         cls: 'x-html-editor-tip'
23398     },
23399     italic : {
23400         title: 'Italic (Ctrl+I)',
23401         text: 'Make the selected text italic.',
23402         cls: 'x-html-editor-tip'
23403     },
23404     ...
23405 </code></pre>
23406     * @type Object
23407      */
23408     buttonTips : {
23409         bold : {
23410             title: 'Bold (Ctrl+B)',
23411             text: 'Make the selected text bold.',
23412             cls: 'x-html-editor-tip'
23413         },
23414         italic : {
23415             title: 'Italic (Ctrl+I)',
23416             text: 'Make the selected text italic.',
23417             cls: 'x-html-editor-tip'
23418         },
23419         underline : {
23420             title: 'Underline (Ctrl+U)',
23421             text: 'Underline the selected text.',
23422             cls: 'x-html-editor-tip'
23423         },
23424         strikethrough : {
23425             title: 'Strikethrough',
23426             text: 'Strikethrough the selected text.',
23427             cls: 'x-html-editor-tip'
23428         },
23429         increasefontsize : {
23430             title: 'Grow Text',
23431             text: 'Increase the font size.',
23432             cls: 'x-html-editor-tip'
23433         },
23434         decreasefontsize : {
23435             title: 'Shrink Text',
23436             text: 'Decrease the font size.',
23437             cls: 'x-html-editor-tip'
23438         },
23439         backcolor : {
23440             title: 'Text Highlight Color',
23441             text: 'Change the background color of the selected text.',
23442             cls: 'x-html-editor-tip'
23443         },
23444         forecolor : {
23445             title: 'Font Color',
23446             text: 'Change the color of the selected text.',
23447             cls: 'x-html-editor-tip'
23448         },
23449         justifyleft : {
23450             title: 'Align Text Left',
23451             text: 'Align text to the left.',
23452             cls: 'x-html-editor-tip'
23453         },
23454         justifycenter : {
23455             title: 'Center Text',
23456             text: 'Center text in the editor.',
23457             cls: 'x-html-editor-tip'
23458         },
23459         justifyright : {
23460             title: 'Align Text Right',
23461             text: 'Align text to the right.',
23462             cls: 'x-html-editor-tip'
23463         },
23464         insertunorderedlist : {
23465             title: 'Bullet List',
23466             text: 'Start a bulleted list.',
23467             cls: 'x-html-editor-tip'
23468         },
23469         insertorderedlist : {
23470             title: 'Numbered List',
23471             text: 'Start a numbered list.',
23472             cls: 'x-html-editor-tip'
23473         },
23474         createlink : {
23475             title: 'Hyperlink',
23476             text: 'Make the selected text a hyperlink.',
23477             cls: 'x-html-editor-tip'
23478         },
23479         sourceedit : {
23480             title: 'Source Edit',
23481             text: 'Switch to source editing mode.',
23482             cls: 'x-html-editor-tip'
23483         }
23484     },
23485     // private
23486     onDestroy : function(){
23487         if(this.rendered){
23488             
23489             this.tb.items.each(function(item){
23490                 if(item.menu){
23491                     item.menu.removeAll();
23492                     if(item.menu.el){
23493                         item.menu.el.destroy();
23494                     }
23495                 }
23496                 item.destroy();
23497             });
23498              
23499         }
23500     },
23501     onFirstFocus: function() {
23502         this.tb.items.each(function(item){
23503            item.enable();
23504         });
23505     }
23506 });
23507
23508
23509
23510
23511 // <script type="text/javascript">
23512 /*
23513  * Based on
23514  * Ext JS Library 1.1.1
23515  * Copyright(c) 2006-2007, Ext JS, LLC.
23516  *  
23517  
23518  */
23519
23520  
23521 /**
23522  * @class Roo.form.HtmlEditor.ToolbarContext
23523  * Context Toolbar
23524  * 
23525  * Usage:
23526  *
23527  new Roo.form.HtmlEditor({
23528     ....
23529     toolbars : [
23530         { xtype: 'ToolbarStandard', styles : {} }
23531         { xtype: 'ToolbarContext', disable : {} }
23532     ]
23533 })
23534
23535      
23536  * 
23537  * @config : {Object} disable List of elements to disable.. (not done yet.)
23538  * @config : {Object} styles  Map of styles available.
23539  * 
23540  */
23541
23542 Roo.form.HtmlEditor.ToolbarContext = function(config)
23543 {
23544     
23545     Roo.apply(this, config);
23546     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23547     // dont call parent... till later.
23548     this.styles = this.styles || {};
23549 }
23550
23551  
23552
23553 Roo.form.HtmlEditor.ToolbarContext.types = {
23554     'IMG' : {
23555         width : {
23556             title: "Width",
23557             width: 40
23558         },
23559         height:  {
23560             title: "Height",
23561             width: 40
23562         },
23563         align: {
23564             title: "Align",
23565             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
23566             width : 80
23567             
23568         },
23569         border: {
23570             title: "Border",
23571             width: 40
23572         },
23573         alt: {
23574             title: "Alt",
23575             width: 120
23576         },
23577         src : {
23578             title: "Src",
23579             width: 220
23580         }
23581         
23582     },
23583     'A' : {
23584         name : {
23585             title: "Name",
23586             width: 50
23587         },
23588         target:  {
23589             title: "Target",
23590             width: 120
23591         },
23592         href:  {
23593             title: "Href",
23594             width: 220
23595         } // border?
23596         
23597     },
23598     'TABLE' : {
23599         rows : {
23600             title: "Rows",
23601             width: 20
23602         },
23603         cols : {
23604             title: "Cols",
23605             width: 20
23606         },
23607         width : {
23608             title: "Width",
23609             width: 40
23610         },
23611         height : {
23612             title: "Height",
23613             width: 40
23614         },
23615         border : {
23616             title: "Border",
23617             width: 20
23618         }
23619     },
23620     'TD' : {
23621         width : {
23622             title: "Width",
23623             width: 40
23624         },
23625         height : {
23626             title: "Height",
23627             width: 40
23628         },   
23629         align: {
23630             title: "Align",
23631             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
23632             width: 80
23633         },
23634         valign: {
23635             title: "Valign",
23636             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
23637             width: 80
23638         },
23639         colspan: {
23640             title: "Colspan",
23641             width: 20
23642             
23643         },
23644          'font-family'  : {
23645             title : "Font",
23646             style : 'fontFamily',
23647             displayField: 'display',
23648             optname : 'font-family',
23649             width: 140
23650         }
23651     },
23652     'INPUT' : {
23653         name : {
23654             title: "name",
23655             width: 120
23656         },
23657         value : {
23658             title: "Value",
23659             width: 120
23660         },
23661         width : {
23662             title: "Width",
23663             width: 40
23664         }
23665     },
23666     'LABEL' : {
23667         'for' : {
23668             title: "For",
23669             width: 120
23670         }
23671     },
23672     'TEXTAREA' : {
23673           name : {
23674             title: "name",
23675             width: 120
23676         },
23677         rows : {
23678             title: "Rows",
23679             width: 20
23680         },
23681         cols : {
23682             title: "Cols",
23683             width: 20
23684         }
23685     },
23686     'SELECT' : {
23687         name : {
23688             title: "name",
23689             width: 120
23690         },
23691         selectoptions : {
23692             title: "Options",
23693             width: 200
23694         }
23695     },
23696     
23697     // should we really allow this??
23698     // should this just be 
23699     'BODY' : {
23700         title : {
23701             title: "Title",
23702             width: 200,
23703             disabled : true
23704         }
23705     },
23706     'SPAN' : {
23707         'font-family'  : {
23708             title : "Font",
23709             style : 'fontFamily',
23710             displayField: 'display',
23711             optname : 'font-family',
23712             width: 140
23713         }
23714     },
23715     'DIV' : {
23716         'font-family'  : {
23717             title : "Font",
23718             style : 'fontFamily',
23719             displayField: 'display',
23720             optname : 'font-family',
23721             width: 140
23722         }
23723     },
23724      'P' : {
23725         'font-family'  : {
23726             title : "Font",
23727             style : 'fontFamily',
23728             displayField: 'display',
23729             optname : 'font-family',
23730             width: 140
23731         }
23732     },
23733     
23734     '*' : {
23735         // empty..
23736     }
23737
23738 };
23739
23740 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
23741 Roo.form.HtmlEditor.ToolbarContext.stores = false;
23742
23743 Roo.form.HtmlEditor.ToolbarContext.options = {
23744         'font-family'  : [ 
23745                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
23746                 [ 'Courier New', 'Courier New'],
23747                 [ 'Tahoma', 'Tahoma'],
23748                 [ 'Times New Roman,serif', 'Times'],
23749                 [ 'Verdana','Verdana' ]
23750         ]
23751 };
23752
23753 // fixme - these need to be configurable..
23754  
23755
23756 //Roo.form.HtmlEditor.ToolbarContext.types
23757
23758
23759 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
23760     
23761     tb: false,
23762     
23763     rendered: false,
23764     
23765     editor : false,
23766     editorcore : false,
23767     /**
23768      * @cfg {Object} disable  List of toolbar elements to disable
23769          
23770      */
23771     disable : false,
23772     /**
23773      * @cfg {Object} styles List of styles 
23774      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
23775      *
23776      * These must be defined in the page, so they get rendered correctly..
23777      * .headline { }
23778      * TD.underline { }
23779      * 
23780      */
23781     styles : false,
23782     
23783     options: false,
23784     
23785     toolbars : false,
23786     
23787     init : function(editor)
23788     {
23789         this.editor = editor;
23790         this.editorcore = editor.editorcore ? editor.editorcore : editor;
23791         var editorcore = this.editorcore;
23792         
23793         var fid = editorcore.frameId;
23794         var etb = this;
23795         function btn(id, toggle, handler){
23796             var xid = fid + '-'+ id ;
23797             return {
23798                 id : xid,
23799                 cmd : id,
23800                 cls : 'x-btn-icon x-edit-'+id,
23801                 enableToggle:toggle !== false,
23802                 scope: editorcore, // was editor...
23803                 handler:handler||editorcore.relayBtnCmd,
23804                 clickEvent:'mousedown',
23805                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
23806                 tabIndex:-1
23807             };
23808         }
23809         // create a new element.
23810         var wdiv = editor.wrap.createChild({
23811                 tag: 'div'
23812             }, editor.wrap.dom.firstChild.nextSibling, true);
23813         
23814         // can we do this more than once??
23815         
23816          // stop form submits
23817       
23818  
23819         // disable everything...
23820         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
23821         this.toolbars = {};
23822            
23823         for (var i in  ty) {
23824           
23825             this.toolbars[i] = this.buildToolbar(ty[i],i);
23826         }
23827         this.tb = this.toolbars.BODY;
23828         this.tb.el.show();
23829         this.buildFooter();
23830         this.footer.show();
23831         editor.on('hide', function( ) { this.footer.hide() }, this);
23832         editor.on('show', function( ) { this.footer.show() }, this);
23833         
23834          
23835         this.rendered = true;
23836         
23837         // the all the btns;
23838         editor.on('editorevent', this.updateToolbar, this);
23839         // other toolbars need to implement this..
23840         //editor.on('editmodechange', this.updateToolbar, this);
23841     },
23842     
23843     
23844     
23845     /**
23846      * Protected method that will not generally be called directly. It triggers
23847      * a toolbar update by reading the markup state of the current selection in the editor.
23848      *
23849      * Note you can force an update by calling on('editorevent', scope, false)
23850      */
23851     updateToolbar: function(editor,ev,sel){
23852
23853         //Roo.log(ev);
23854         // capture mouse up - this is handy for selecting images..
23855         // perhaps should go somewhere else...
23856         if(!this.editorcore.activated){
23857              this.editor.onFirstFocus();
23858             return;
23859         }
23860         
23861         
23862         
23863         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
23864         // selectNode - might want to handle IE?
23865         if (ev &&
23866             (ev.type == 'mouseup' || ev.type == 'click' ) &&
23867             ev.target && ev.target.tagName == 'IMG') {
23868             // they have click on an image...
23869             // let's see if we can change the selection...
23870             sel = ev.target;
23871          
23872               var nodeRange = sel.ownerDocument.createRange();
23873             try {
23874                 nodeRange.selectNode(sel);
23875             } catch (e) {
23876                 nodeRange.selectNodeContents(sel);
23877             }
23878             //nodeRange.collapse(true);
23879             var s = this.editorcore.win.getSelection();
23880             s.removeAllRanges();
23881             s.addRange(nodeRange);
23882         }  
23883         
23884       
23885         var updateFooter = sel ? false : true;
23886         
23887         
23888         var ans = this.editorcore.getAllAncestors();
23889         
23890         // pick
23891         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
23892         
23893         if (!sel) { 
23894             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
23895             sel = sel ? sel : this.editorcore.doc.body;
23896             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
23897             
23898         }
23899         // pick a menu that exists..
23900         var tn = sel.tagName.toUpperCase();
23901         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
23902         
23903         tn = sel.tagName.toUpperCase();
23904         
23905         var lastSel = this.tb.selectedNode;
23906         
23907         this.tb.selectedNode = sel;
23908         
23909         // if current menu does not match..
23910         
23911         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
23912                 
23913             this.tb.el.hide();
23914             ///console.log("show: " + tn);
23915             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
23916             this.tb.el.show();
23917             // update name
23918             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
23919             
23920             
23921             // update attributes
23922             if (this.tb.fields) {
23923                 this.tb.fields.each(function(e) {
23924                     if (e.stylename) {
23925                         e.setValue(sel.style[e.stylename]);
23926                         return;
23927                     } 
23928                    e.setValue(sel.getAttribute(e.attrname));
23929                 });
23930             }
23931             
23932             var hasStyles = false;
23933             for(var i in this.styles) {
23934                 hasStyles = true;
23935                 break;
23936             }
23937             
23938             // update styles
23939             if (hasStyles) { 
23940                 var st = this.tb.fields.item(0);
23941                 
23942                 st.store.removeAll();
23943                
23944                 
23945                 var cn = sel.className.split(/\s+/);
23946                 
23947                 var avs = [];
23948                 if (this.styles['*']) {
23949                     
23950                     Roo.each(this.styles['*'], function(v) {
23951                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
23952                     });
23953                 }
23954                 if (this.styles[tn]) { 
23955                     Roo.each(this.styles[tn], function(v) {
23956                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
23957                     });
23958                 }
23959                 
23960                 st.store.loadData(avs);
23961                 st.collapse();
23962                 st.setValue(cn);
23963             }
23964             // flag our selected Node.
23965             this.tb.selectedNode = sel;
23966            
23967            
23968             Roo.menu.MenuMgr.hideAll();
23969
23970         }
23971         
23972         if (!updateFooter) {
23973             //this.footDisp.dom.innerHTML = ''; 
23974             return;
23975         }
23976         // update the footer
23977         //
23978         var html = '';
23979         
23980         this.footerEls = ans.reverse();
23981         Roo.each(this.footerEls, function(a,i) {
23982             if (!a) { return; }
23983             html += html.length ? ' &gt; '  :  '';
23984             
23985             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
23986             
23987         });
23988        
23989         // 
23990         var sz = this.footDisp.up('td').getSize();
23991         this.footDisp.dom.style.width = (sz.width -10) + 'px';
23992         this.footDisp.dom.style.marginLeft = '5px';
23993         
23994         this.footDisp.dom.style.overflow = 'hidden';
23995         
23996         this.footDisp.dom.innerHTML = html;
23997             
23998         //this.editorsyncValue();
23999     },
24000      
24001     
24002    
24003        
24004     // private
24005     onDestroy : function(){
24006         if(this.rendered){
24007             
24008             this.tb.items.each(function(item){
24009                 if(item.menu){
24010                     item.menu.removeAll();
24011                     if(item.menu.el){
24012                         item.menu.el.destroy();
24013                     }
24014                 }
24015                 item.destroy();
24016             });
24017              
24018         }
24019     },
24020     onFirstFocus: function() {
24021         // need to do this for all the toolbars..
24022         this.tb.items.each(function(item){
24023            item.enable();
24024         });
24025     },
24026     buildToolbar: function(tlist, nm)
24027     {
24028         var editor = this.editor;
24029         var editorcore = this.editorcore;
24030          // create a new element.
24031         var wdiv = editor.wrap.createChild({
24032                 tag: 'div'
24033             }, editor.wrap.dom.firstChild.nextSibling, true);
24034         
24035        
24036         var tb = new Roo.Toolbar(wdiv);
24037         // add the name..
24038         
24039         tb.add(nm+ ":&nbsp;");
24040         
24041         var styles = [];
24042         for(var i in this.styles) {
24043             styles.push(i);
24044         }
24045         
24046         // styles...
24047         if (styles && styles.length) {
24048             
24049             // this needs a multi-select checkbox...
24050             tb.addField( new Roo.form.ComboBox({
24051                 store: new Roo.data.SimpleStore({
24052                     id : 'val',
24053                     fields: ['val', 'selected'],
24054                     data : [] 
24055                 }),
24056                 name : '-roo-edit-className',
24057                 attrname : 'className',
24058                 displayField: 'val',
24059                 typeAhead: false,
24060                 mode: 'local',
24061                 editable : false,
24062                 triggerAction: 'all',
24063                 emptyText:'Select Style',
24064                 selectOnFocus:true,
24065                 width: 130,
24066                 listeners : {
24067                     'select': function(c, r, i) {
24068                         // initial support only for on class per el..
24069                         tb.selectedNode.className =  r ? r.get('val') : '';
24070                         editorcore.syncValue();
24071                     }
24072                 }
24073     
24074             }));
24075         }
24076         
24077         var tbc = Roo.form.HtmlEditor.ToolbarContext;
24078         var tbops = tbc.options;
24079         
24080         for (var i in tlist) {
24081             
24082             var item = tlist[i];
24083             tb.add(item.title + ":&nbsp;");
24084             
24085             
24086             //optname == used so you can configure the options available..
24087             var opts = item.opts ? item.opts : false;
24088             if (item.optname) {
24089                 opts = tbops[item.optname];
24090            
24091             }
24092             
24093             if (opts) {
24094                 // opts == pulldown..
24095                 tb.addField( new Roo.form.ComboBox({
24096                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
24097                         id : 'val',
24098                         fields: ['val', 'display'],
24099                         data : opts  
24100                     }),
24101                     name : '-roo-edit-' + i,
24102                     attrname : i,
24103                     stylename : item.style ? item.style : false,
24104                     displayField: item.displayField ? item.displayField : 'val',
24105                     valueField :  'val',
24106                     typeAhead: false,
24107                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
24108                     editable : false,
24109                     triggerAction: 'all',
24110                     emptyText:'Select',
24111                     selectOnFocus:true,
24112                     width: item.width ? item.width  : 130,
24113                     listeners : {
24114                         'select': function(c, r, i) {
24115                             if (c.stylename) {
24116                                 tb.selectedNode.style[c.stylename] =  r.get('val');
24117                                 return;
24118                             }
24119                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
24120                         }
24121                     }
24122
24123                 }));
24124                 continue;
24125                     
24126                  
24127                 
24128                 tb.addField( new Roo.form.TextField({
24129                     name: i,
24130                     width: 100,
24131                     //allowBlank:false,
24132                     value: ''
24133                 }));
24134                 continue;
24135             }
24136             tb.addField( new Roo.form.TextField({
24137                 name: '-roo-edit-' + i,
24138                 attrname : i,
24139                 
24140                 width: item.width,
24141                 //allowBlank:true,
24142                 value: '',
24143                 listeners: {
24144                     'change' : function(f, nv, ov) {
24145                         tb.selectedNode.setAttribute(f.attrname, nv);
24146                         editorcore.syncValue();
24147                     }
24148                 }
24149             }));
24150              
24151         }
24152         
24153         var _this = this;
24154         
24155         if(nm == 'BODY'){
24156             tb.addSeparator();
24157         
24158             tb.addButton( {
24159                 text: 'Stylesheets',
24160
24161                 listeners : {
24162                     click : function ()
24163                     {
24164                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
24165                     }
24166                 }
24167             });
24168         }
24169         
24170         tb.addFill();
24171         tb.addButton( {
24172             text: 'Remove Tag',
24173     
24174             listeners : {
24175                 click : function ()
24176                 {
24177                     // remove
24178                     // undo does not work.
24179                      
24180                     var sn = tb.selectedNode;
24181                     
24182                     var pn = sn.parentNode;
24183                     
24184                     var stn =  sn.childNodes[0];
24185                     var en = sn.childNodes[sn.childNodes.length - 1 ];
24186                     while (sn.childNodes.length) {
24187                         var node = sn.childNodes[0];
24188                         sn.removeChild(node);
24189                         //Roo.log(node);
24190                         pn.insertBefore(node, sn);
24191                         
24192                     }
24193                     pn.removeChild(sn);
24194                     var range = editorcore.createRange();
24195         
24196                     range.setStart(stn,0);
24197                     range.setEnd(en,0); //????
24198                     //range.selectNode(sel);
24199                     
24200                     
24201                     var selection = editorcore.getSelection();
24202                     selection.removeAllRanges();
24203                     selection.addRange(range);
24204                     
24205                     
24206                     
24207                     //_this.updateToolbar(null, null, pn);
24208                     _this.updateToolbar(null, null, null);
24209                     _this.footDisp.dom.innerHTML = ''; 
24210                 }
24211             }
24212             
24213                     
24214                 
24215             
24216         });
24217         
24218         
24219         tb.el.on('click', function(e){
24220             e.preventDefault(); // what does this do?
24221         });
24222         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
24223         tb.el.hide();
24224         tb.name = nm;
24225         // dont need to disable them... as they will get hidden
24226         return tb;
24227          
24228         
24229     },
24230     buildFooter : function()
24231     {
24232         
24233         var fel = this.editor.wrap.createChild();
24234         this.footer = new Roo.Toolbar(fel);
24235         // toolbar has scrolly on left / right?
24236         var footDisp= new Roo.Toolbar.Fill();
24237         var _t = this;
24238         this.footer.add(
24239             {
24240                 text : '&lt;',
24241                 xtype: 'Button',
24242                 handler : function() {
24243                     _t.footDisp.scrollTo('left',0,true)
24244                 }
24245             }
24246         );
24247         this.footer.add( footDisp );
24248         this.footer.add( 
24249             {
24250                 text : '&gt;',
24251                 xtype: 'Button',
24252                 handler : function() {
24253                     // no animation..
24254                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
24255                 }
24256             }
24257         );
24258         var fel = Roo.get(footDisp.el);
24259         fel.addClass('x-editor-context');
24260         this.footDispWrap = fel; 
24261         this.footDispWrap.overflow  = 'hidden';
24262         
24263         this.footDisp = fel.createChild();
24264         this.footDispWrap.on('click', this.onContextClick, this)
24265         
24266         
24267     },
24268     onContextClick : function (ev,dom)
24269     {
24270         ev.preventDefault();
24271         var  cn = dom.className;
24272         //Roo.log(cn);
24273         if (!cn.match(/x-ed-loc-/)) {
24274             return;
24275         }
24276         var n = cn.split('-').pop();
24277         var ans = this.footerEls;
24278         var sel = ans[n];
24279         
24280          // pick
24281         var range = this.editorcore.createRange();
24282         
24283         range.selectNodeContents(sel);
24284         //range.selectNode(sel);
24285         
24286         
24287         var selection = this.editorcore.getSelection();
24288         selection.removeAllRanges();
24289         selection.addRange(range);
24290         
24291         
24292         
24293         this.updateToolbar(null, null, sel);
24294         
24295         
24296     }
24297     
24298     
24299     
24300     
24301     
24302 });
24303
24304
24305
24306
24307
24308 /*
24309  * Based on:
24310  * Ext JS Library 1.1.1
24311  * Copyright(c) 2006-2007, Ext JS, LLC.
24312  *
24313  * Originally Released Under LGPL - original licence link has changed is not relivant.
24314  *
24315  * Fork - LGPL
24316  * <script type="text/javascript">
24317  */
24318  
24319 /**
24320  * @class Roo.form.BasicForm
24321  * @extends Roo.util.Observable
24322  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
24323  * @constructor
24324  * @param {String/HTMLElement/Roo.Element} el The form element or its id
24325  * @param {Object} config Configuration options
24326  */
24327 Roo.form.BasicForm = function(el, config){
24328     this.allItems = [];
24329     this.childForms = [];
24330     Roo.apply(this, config);
24331     /*
24332      * The Roo.form.Field items in this form.
24333      * @type MixedCollection
24334      */
24335      
24336      
24337     this.items = new Roo.util.MixedCollection(false, function(o){
24338         return o.id || (o.id = Roo.id());
24339     });
24340     this.addEvents({
24341         /**
24342          * @event beforeaction
24343          * Fires before any action is performed. Return false to cancel the action.
24344          * @param {Form} this
24345          * @param {Action} action The action to be performed
24346          */
24347         beforeaction: true,
24348         /**
24349          * @event actionfailed
24350          * Fires when an action fails.
24351          * @param {Form} this
24352          * @param {Action} action The action that failed
24353          */
24354         actionfailed : true,
24355         /**
24356          * @event actioncomplete
24357          * Fires when an action is completed.
24358          * @param {Form} this
24359          * @param {Action} action The action that completed
24360          */
24361         actioncomplete : true
24362     });
24363     if(el){
24364         this.initEl(el);
24365     }
24366     Roo.form.BasicForm.superclass.constructor.call(this);
24367 };
24368
24369 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
24370     /**
24371      * @cfg {String} method
24372      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
24373      */
24374     /**
24375      * @cfg {DataReader} reader
24376      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
24377      * This is optional as there is built-in support for processing JSON.
24378      */
24379     /**
24380      * @cfg {DataReader} errorReader
24381      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
24382      * This is completely optional as there is built-in support for processing JSON.
24383      */
24384     /**
24385      * @cfg {String} url
24386      * The URL to use for form actions if one isn't supplied in the action options.
24387      */
24388     /**
24389      * @cfg {Boolean} fileUpload
24390      * Set to true if this form is a file upload.
24391      */
24392      
24393     /**
24394      * @cfg {Object} baseParams
24395      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
24396      */
24397      /**
24398      
24399     /**
24400      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
24401      */
24402     timeout: 30,
24403
24404     // private
24405     activeAction : null,
24406
24407     /**
24408      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
24409      * or setValues() data instead of when the form was first created.
24410      */
24411     trackResetOnLoad : false,
24412     
24413     
24414     /**
24415      * childForms - used for multi-tab forms
24416      * @type {Array}
24417      */
24418     childForms : false,
24419     
24420     /**
24421      * allItems - full list of fields.
24422      * @type {Array}
24423      */
24424     allItems : false,
24425     
24426     /**
24427      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
24428      * element by passing it or its id or mask the form itself by passing in true.
24429      * @type Mixed
24430      */
24431     waitMsgTarget : false,
24432     
24433     /**
24434      * @type Boolean
24435      */
24436     disableMask : false,
24437
24438     // private
24439     initEl : function(el){
24440         this.el = Roo.get(el);
24441         this.id = this.el.id || Roo.id();
24442         this.el.on('submit', this.onSubmit, this);
24443         this.el.addClass('x-form');
24444     },
24445
24446     // private
24447     onSubmit : function(e){
24448         e.stopEvent();
24449     },
24450
24451     /**
24452      * Returns true if client-side validation on the form is successful.
24453      * @return Boolean
24454      */
24455     isValid : function(){
24456         var valid = true;
24457         this.items.each(function(f){
24458            if(!f.validate()){
24459                valid = false;
24460            }
24461         });
24462         return valid;
24463     },
24464
24465     /**
24466      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
24467      * @return Boolean
24468      */
24469     isDirty : function(){
24470         var dirty = false;
24471         this.items.each(function(f){
24472            if(f.isDirty()){
24473                dirty = true;
24474                return false;
24475            }
24476         });
24477         return dirty;
24478     },
24479     
24480     /**
24481      * Returns true if any fields in this form have changed since their original load. (New version)
24482      * @return Boolean
24483      */
24484     
24485     hasChanged : function()
24486     {
24487         var dirty = false;
24488         this.items.each(function(f){
24489            if(f.hasChanged()){
24490                dirty = true;
24491                return false;
24492            }
24493         });
24494         return dirty;
24495         
24496     },
24497     /**
24498      * Resets all hasChanged to 'false' -
24499      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
24500      * So hasChanged storage is only to be used for this purpose
24501      * @return Boolean
24502      */
24503     resetHasChanged : function()
24504     {
24505         this.items.each(function(f){
24506            f.resetHasChanged();
24507         });
24508         
24509     },
24510     
24511     
24512     /**
24513      * Performs a predefined action (submit or load) or custom actions you define on this form.
24514      * @param {String} actionName The name of the action type
24515      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
24516      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
24517      * accept other config options):
24518      * <pre>
24519 Property          Type             Description
24520 ----------------  ---------------  ----------------------------------------------------------------------------------
24521 url               String           The url for the action (defaults to the form's url)
24522 method            String           The form method to use (defaults to the form's method, or POST if not defined)
24523 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
24524 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
24525                                    validate the form on the client (defaults to false)
24526      * </pre>
24527      * @return {BasicForm} this
24528      */
24529     doAction : function(action, options){
24530         if(typeof action == 'string'){
24531             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
24532         }
24533         if(this.fireEvent('beforeaction', this, action) !== false){
24534             this.beforeAction(action);
24535             action.run.defer(100, action);
24536         }
24537         return this;
24538     },
24539
24540     /**
24541      * Shortcut to do a submit action.
24542      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
24543      * @return {BasicForm} this
24544      */
24545     submit : function(options){
24546         this.doAction('submit', options);
24547         return this;
24548     },
24549
24550     /**
24551      * Shortcut to do a load action.
24552      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
24553      * @return {BasicForm} this
24554      */
24555     load : function(options){
24556         this.doAction('load', options);
24557         return this;
24558     },
24559
24560     /**
24561      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
24562      * @param {Record} record The record to edit
24563      * @return {BasicForm} this
24564      */
24565     updateRecord : function(record){
24566         record.beginEdit();
24567         var fs = record.fields;
24568         fs.each(function(f){
24569             var field = this.findField(f.name);
24570             if(field){
24571                 record.set(f.name, field.getValue());
24572             }
24573         }, this);
24574         record.endEdit();
24575         return this;
24576     },
24577
24578     /**
24579      * Loads an Roo.data.Record into this form.
24580      * @param {Record} record The record to load
24581      * @return {BasicForm} this
24582      */
24583     loadRecord : function(record){
24584         this.setValues(record.data);
24585         return this;
24586     },
24587
24588     // private
24589     beforeAction : function(action){
24590         var o = action.options;
24591         
24592         if(!this.disableMask) {
24593             if(this.waitMsgTarget === true){
24594                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
24595             }else if(this.waitMsgTarget){
24596                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
24597                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
24598             }else {
24599                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
24600             }
24601         }
24602         
24603          
24604     },
24605
24606     // private
24607     afterAction : function(action, success){
24608         this.activeAction = null;
24609         var o = action.options;
24610         
24611         if(!this.disableMask) {
24612             if(this.waitMsgTarget === true){
24613                 this.el.unmask();
24614             }else if(this.waitMsgTarget){
24615                 this.waitMsgTarget.unmask();
24616             }else{
24617                 Roo.MessageBox.updateProgress(1);
24618                 Roo.MessageBox.hide();
24619             }
24620         }
24621         
24622         if(success){
24623             if(o.reset){
24624                 this.reset();
24625             }
24626             Roo.callback(o.success, o.scope, [this, action]);
24627             this.fireEvent('actioncomplete', this, action);
24628             
24629         }else{
24630             
24631             // failure condition..
24632             // we have a scenario where updates need confirming.
24633             // eg. if a locking scenario exists..
24634             // we look for { errors : { needs_confirm : true }} in the response.
24635             if (
24636                 (typeof(action.result) != 'undefined')  &&
24637                 (typeof(action.result.errors) != 'undefined')  &&
24638                 (typeof(action.result.errors.needs_confirm) != 'undefined')
24639            ){
24640                 var _t = this;
24641                 Roo.MessageBox.confirm(
24642                     "Change requires confirmation",
24643                     action.result.errorMsg,
24644                     function(r) {
24645                         if (r != 'yes') {
24646                             return;
24647                         }
24648                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
24649                     }
24650                     
24651                 );
24652                 
24653                 
24654                 
24655                 return;
24656             }
24657             
24658             Roo.callback(o.failure, o.scope, [this, action]);
24659             // show an error message if no failed handler is set..
24660             if (!this.hasListener('actionfailed')) {
24661                 Roo.MessageBox.alert("Error",
24662                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
24663                         action.result.errorMsg :
24664                         "Saving Failed, please check your entries or try again"
24665                 );
24666             }
24667             
24668             this.fireEvent('actionfailed', this, action);
24669         }
24670         
24671     },
24672
24673     /**
24674      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
24675      * @param {String} id The value to search for
24676      * @return Field
24677      */
24678     findField : function(id){
24679         var field = this.items.get(id);
24680         if(!field){
24681             this.items.each(function(f){
24682                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
24683                     field = f;
24684                     return false;
24685                 }
24686             });
24687         }
24688         return field || null;
24689     },
24690
24691     /**
24692      * Add a secondary form to this one, 
24693      * Used to provide tabbed forms. One form is primary, with hidden values 
24694      * which mirror the elements from the other forms.
24695      * 
24696      * @param {Roo.form.Form} form to add.
24697      * 
24698      */
24699     addForm : function(form)
24700     {
24701        
24702         if (this.childForms.indexOf(form) > -1) {
24703             // already added..
24704             return;
24705         }
24706         this.childForms.push(form);
24707         var n = '';
24708         Roo.each(form.allItems, function (fe) {
24709             
24710             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
24711             if (this.findField(n)) { // already added..
24712                 return;
24713             }
24714             var add = new Roo.form.Hidden({
24715                 name : n
24716             });
24717             add.render(this.el);
24718             
24719             this.add( add );
24720         }, this);
24721         
24722     },
24723     /**
24724      * Mark fields in this form invalid in bulk.
24725      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
24726      * @return {BasicForm} this
24727      */
24728     markInvalid : function(errors){
24729         if(errors instanceof Array){
24730             for(var i = 0, len = errors.length; i < len; i++){
24731                 var fieldError = errors[i];
24732                 var f = this.findField(fieldError.id);
24733                 if(f){
24734                     f.markInvalid(fieldError.msg);
24735                 }
24736             }
24737         }else{
24738             var field, id;
24739             for(id in errors){
24740                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
24741                     field.markInvalid(errors[id]);
24742                 }
24743             }
24744         }
24745         Roo.each(this.childForms || [], function (f) {
24746             f.markInvalid(errors);
24747         });
24748         
24749         return this;
24750     },
24751
24752     /**
24753      * Set values for fields in this form in bulk.
24754      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
24755      * @return {BasicForm} this
24756      */
24757     setValues : function(values){
24758         if(values instanceof Array){ // array of objects
24759             for(var i = 0, len = values.length; i < len; i++){
24760                 var v = values[i];
24761                 var f = this.findField(v.id);
24762                 if(f){
24763                     f.setValue(v.value);
24764                     if(this.trackResetOnLoad){
24765                         f.originalValue = f.getValue();
24766                     }
24767                 }
24768             }
24769         }else{ // object hash
24770             var field, id;
24771             for(id in values){
24772                 if(typeof values[id] != 'function' && (field = this.findField(id))){
24773                     
24774                     if (field.setFromData && 
24775                         field.valueField && 
24776                         field.displayField &&
24777                         // combos' with local stores can 
24778                         // be queried via setValue()
24779                         // to set their value..
24780                         (field.store && !field.store.isLocal)
24781                         ) {
24782                         // it's a combo
24783                         var sd = { };
24784                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
24785                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
24786                         field.setFromData(sd);
24787                         
24788                     } else {
24789                         field.setValue(values[id]);
24790                     }
24791                     
24792                     
24793                     if(this.trackResetOnLoad){
24794                         field.originalValue = field.getValue();
24795                     }
24796                 }
24797             }
24798         }
24799         this.resetHasChanged();
24800         
24801         
24802         Roo.each(this.childForms || [], function (f) {
24803             f.setValues(values);
24804             f.resetHasChanged();
24805         });
24806                 
24807         return this;
24808     },
24809
24810     /**
24811      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
24812      * they are returned as an array.
24813      * @param {Boolean} asString
24814      * @return {Object}
24815      */
24816     getValues : function(asString){
24817         if (this.childForms) {
24818             // copy values from the child forms
24819             Roo.each(this.childForms, function (f) {
24820                 this.setValues(f.getValues());
24821             }, this);
24822         }
24823         
24824         
24825         
24826         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
24827         if(asString === true){
24828             return fs;
24829         }
24830         return Roo.urlDecode(fs);
24831     },
24832     
24833     /**
24834      * Returns the fields in this form as an object with key/value pairs. 
24835      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
24836      * @return {Object}
24837      */
24838     getFieldValues : function(with_hidden)
24839     {
24840         if (this.childForms) {
24841             // copy values from the child forms
24842             // should this call getFieldValues - probably not as we do not currently copy
24843             // hidden fields when we generate..
24844             Roo.each(this.childForms, function (f) {
24845                 this.setValues(f.getValues());
24846             }, this);
24847         }
24848         
24849         var ret = {};
24850         this.items.each(function(f){
24851             if (!f.getName()) {
24852                 return;
24853             }
24854             var v = f.getValue();
24855             if (f.inputType =='radio') {
24856                 if (typeof(ret[f.getName()]) == 'undefined') {
24857                     ret[f.getName()] = ''; // empty..
24858                 }
24859                 
24860                 if (!f.el.dom.checked) {
24861                     return;
24862                     
24863                 }
24864                 v = f.el.dom.value;
24865                 
24866             }
24867             
24868             // not sure if this supported any more..
24869             if ((typeof(v) == 'object') && f.getRawValue) {
24870                 v = f.getRawValue() ; // dates..
24871             }
24872             // combo boxes where name != hiddenName...
24873             if (f.name != f.getName()) {
24874                 ret[f.name] = f.getRawValue();
24875             }
24876             ret[f.getName()] = v;
24877         });
24878         
24879         return ret;
24880     },
24881
24882     /**
24883      * Clears all invalid messages in this form.
24884      * @return {BasicForm} this
24885      */
24886     clearInvalid : function(){
24887         this.items.each(function(f){
24888            f.clearInvalid();
24889         });
24890         
24891         Roo.each(this.childForms || [], function (f) {
24892             f.clearInvalid();
24893         });
24894         
24895         
24896         return this;
24897     },
24898
24899     /**
24900      * Resets this form.
24901      * @return {BasicForm} this
24902      */
24903     reset : function(){
24904         this.items.each(function(f){
24905             f.reset();
24906         });
24907         
24908         Roo.each(this.childForms || [], function (f) {
24909             f.reset();
24910         });
24911         this.resetHasChanged();
24912         
24913         return this;
24914     },
24915
24916     /**
24917      * Add Roo.form components to this form.
24918      * @param {Field} field1
24919      * @param {Field} field2 (optional)
24920      * @param {Field} etc (optional)
24921      * @return {BasicForm} this
24922      */
24923     add : function(){
24924         this.items.addAll(Array.prototype.slice.call(arguments, 0));
24925         return this;
24926     },
24927
24928
24929     /**
24930      * Removes a field from the items collection (does NOT remove its markup).
24931      * @param {Field} field
24932      * @return {BasicForm} this
24933      */
24934     remove : function(field){
24935         this.items.remove(field);
24936         return this;
24937     },
24938
24939     /**
24940      * Looks at the fields in this form, checks them for an id attribute,
24941      * and calls applyTo on the existing dom element with that id.
24942      * @return {BasicForm} this
24943      */
24944     render : function(){
24945         this.items.each(function(f){
24946             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
24947                 f.applyTo(f.id);
24948             }
24949         });
24950         return this;
24951     },
24952
24953     /**
24954      * Calls {@link Ext#apply} for all fields in this form with the passed object.
24955      * @param {Object} values
24956      * @return {BasicForm} this
24957      */
24958     applyToFields : function(o){
24959         this.items.each(function(f){
24960            Roo.apply(f, o);
24961         });
24962         return this;
24963     },
24964
24965     /**
24966      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
24967      * @param {Object} values
24968      * @return {BasicForm} this
24969      */
24970     applyIfToFields : function(o){
24971         this.items.each(function(f){
24972            Roo.applyIf(f, o);
24973         });
24974         return this;
24975     }
24976 });
24977
24978 // back compat
24979 Roo.BasicForm = Roo.form.BasicForm;/*
24980  * Based on:
24981  * Ext JS Library 1.1.1
24982  * Copyright(c) 2006-2007, Ext JS, LLC.
24983  *
24984  * Originally Released Under LGPL - original licence link has changed is not relivant.
24985  *
24986  * Fork - LGPL
24987  * <script type="text/javascript">
24988  */
24989
24990 /**
24991  * @class Roo.form.Form
24992  * @extends Roo.form.BasicForm
24993  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
24994  * @constructor
24995  * @param {Object} config Configuration options
24996  */
24997 Roo.form.Form = function(config){
24998     var xitems =  [];
24999     if (config.items) {
25000         xitems = config.items;
25001         delete config.items;
25002     }
25003    
25004     
25005     Roo.form.Form.superclass.constructor.call(this, null, config);
25006     this.url = this.url || this.action;
25007     if(!this.root){
25008         this.root = new Roo.form.Layout(Roo.applyIf({
25009             id: Roo.id()
25010         }, config));
25011     }
25012     this.active = this.root;
25013     /**
25014      * Array of all the buttons that have been added to this form via {@link addButton}
25015      * @type Array
25016      */
25017     this.buttons = [];
25018     this.allItems = [];
25019     this.addEvents({
25020         /**
25021          * @event clientvalidation
25022          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
25023          * @param {Form} this
25024          * @param {Boolean} valid true if the form has passed client-side validation
25025          */
25026         clientvalidation: true,
25027         /**
25028          * @event rendered
25029          * Fires when the form is rendered
25030          * @param {Roo.form.Form} form
25031          */
25032         rendered : true
25033     });
25034     
25035     if (this.progressUrl) {
25036             // push a hidden field onto the list of fields..
25037             this.addxtype( {
25038                     xns: Roo.form, 
25039                     xtype : 'Hidden', 
25040                     name : 'UPLOAD_IDENTIFIER' 
25041             });
25042         }
25043         
25044     
25045     Roo.each(xitems, this.addxtype, this);
25046     
25047     
25048     
25049 };
25050
25051 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
25052     /**
25053      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
25054      */
25055     /**
25056      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
25057      */
25058     /**
25059      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
25060      */
25061     buttonAlign:'center',
25062
25063     /**
25064      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
25065      */
25066     minButtonWidth:75,
25067
25068     /**
25069      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
25070      * This property cascades to child containers if not set.
25071      */
25072     labelAlign:'left',
25073
25074     /**
25075      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
25076      * fires a looping event with that state. This is required to bind buttons to the valid
25077      * state using the config value formBind:true on the button.
25078      */
25079     monitorValid : false,
25080
25081     /**
25082      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
25083      */
25084     monitorPoll : 200,
25085     
25086     /**
25087      * @cfg {String} progressUrl - Url to return progress data 
25088      */
25089     
25090     progressUrl : false,
25091     /**
25092      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
25093      * sending a formdata with extra parameters - eg uploaded elements.
25094      */
25095     
25096     formData : false,
25097     
25098     /**
25099      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
25100      * fields are added and the column is closed. If no fields are passed the column remains open
25101      * until end() is called.
25102      * @param {Object} config The config to pass to the column
25103      * @param {Field} field1 (optional)
25104      * @param {Field} field2 (optional)
25105      * @param {Field} etc (optional)
25106      * @return Column The column container object
25107      */
25108     column : function(c){
25109         var col = new Roo.form.Column(c);
25110         this.start(col);
25111         if(arguments.length > 1){ // duplicate code required because of Opera
25112             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25113             this.end();
25114         }
25115         return col;
25116     },
25117
25118     /**
25119      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
25120      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
25121      * until end() is called.
25122      * @param {Object} config The config to pass to the fieldset
25123      * @param {Field} field1 (optional)
25124      * @param {Field} field2 (optional)
25125      * @param {Field} etc (optional)
25126      * @return FieldSet The fieldset container object
25127      */
25128     fieldset : function(c){
25129         var fs = new Roo.form.FieldSet(c);
25130         this.start(fs);
25131         if(arguments.length > 1){ // duplicate code required because of Opera
25132             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25133             this.end();
25134         }
25135         return fs;
25136     },
25137
25138     /**
25139      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
25140      * fields are added and the container is closed. If no fields are passed the container remains open
25141      * until end() is called.
25142      * @param {Object} config The config to pass to the Layout
25143      * @param {Field} field1 (optional)
25144      * @param {Field} field2 (optional)
25145      * @param {Field} etc (optional)
25146      * @return Layout The container object
25147      */
25148     container : function(c){
25149         var l = new Roo.form.Layout(c);
25150         this.start(l);
25151         if(arguments.length > 1){ // duplicate code required because of Opera
25152             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25153             this.end();
25154         }
25155         return l;
25156     },
25157
25158     /**
25159      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
25160      * @param {Object} container A Roo.form.Layout or subclass of Layout
25161      * @return {Form} this
25162      */
25163     start : function(c){
25164         // cascade label info
25165         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
25166         this.active.stack.push(c);
25167         c.ownerCt = this.active;
25168         this.active = c;
25169         return this;
25170     },
25171
25172     /**
25173      * Closes the current open container
25174      * @return {Form} this
25175      */
25176     end : function(){
25177         if(this.active == this.root){
25178             return this;
25179         }
25180         this.active = this.active.ownerCt;
25181         return this;
25182     },
25183
25184     /**
25185      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
25186      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
25187      * as the label of the field.
25188      * @param {Field} field1
25189      * @param {Field} field2 (optional)
25190      * @param {Field} etc. (optional)
25191      * @return {Form} this
25192      */
25193     add : function(){
25194         this.active.stack.push.apply(this.active.stack, arguments);
25195         this.allItems.push.apply(this.allItems,arguments);
25196         var r = [];
25197         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
25198             if(a[i].isFormField){
25199                 r.push(a[i]);
25200             }
25201         }
25202         if(r.length > 0){
25203             Roo.form.Form.superclass.add.apply(this, r);
25204         }
25205         return this;
25206     },
25207     
25208
25209     
25210     
25211     
25212      /**
25213      * Find any element that has been added to a form, using it's ID or name
25214      * This can include framesets, columns etc. along with regular fields..
25215      * @param {String} id - id or name to find.
25216      
25217      * @return {Element} e - or false if nothing found.
25218      */
25219     findbyId : function(id)
25220     {
25221         var ret = false;
25222         if (!id) {
25223             return ret;
25224         }
25225         Roo.each(this.allItems, function(f){
25226             if (f.id == id || f.name == id ){
25227                 ret = f;
25228                 return false;
25229             }
25230         });
25231         return ret;
25232     },
25233
25234     
25235     
25236     /**
25237      * Render this form into the passed container. This should only be called once!
25238      * @param {String/HTMLElement/Element} container The element this component should be rendered into
25239      * @return {Form} this
25240      */
25241     render : function(ct)
25242     {
25243         
25244         
25245         
25246         ct = Roo.get(ct);
25247         var o = this.autoCreate || {
25248             tag: 'form',
25249             method : this.method || 'POST',
25250             id : this.id || Roo.id()
25251         };
25252         this.initEl(ct.createChild(o));
25253
25254         this.root.render(this.el);
25255         
25256        
25257              
25258         this.items.each(function(f){
25259             f.render('x-form-el-'+f.id);
25260         });
25261
25262         if(this.buttons.length > 0){
25263             // tables are required to maintain order and for correct IE layout
25264             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
25265                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
25266                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
25267             }}, null, true);
25268             var tr = tb.getElementsByTagName('tr')[0];
25269             for(var i = 0, len = this.buttons.length; i < len; i++) {
25270                 var b = this.buttons[i];
25271                 var td = document.createElement('td');
25272                 td.className = 'x-form-btn-td';
25273                 b.render(tr.appendChild(td));
25274             }
25275         }
25276         if(this.monitorValid){ // initialize after render
25277             this.startMonitoring();
25278         }
25279         this.fireEvent('rendered', this);
25280         return this;
25281     },
25282
25283     /**
25284      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
25285      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
25286      * object or a valid Roo.DomHelper element config
25287      * @param {Function} handler The function called when the button is clicked
25288      * @param {Object} scope (optional) The scope of the handler function
25289      * @return {Roo.Button}
25290      */
25291     addButton : function(config, handler, scope){
25292         var bc = {
25293             handler: handler,
25294             scope: scope,
25295             minWidth: this.minButtonWidth,
25296             hideParent:true
25297         };
25298         if(typeof config == "string"){
25299             bc.text = config;
25300         }else{
25301             Roo.apply(bc, config);
25302         }
25303         var btn = new Roo.Button(null, bc);
25304         this.buttons.push(btn);
25305         return btn;
25306     },
25307
25308      /**
25309      * Adds a series of form elements (using the xtype property as the factory method.
25310      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
25311      * @param {Object} config 
25312      */
25313     
25314     addxtype : function()
25315     {
25316         var ar = Array.prototype.slice.call(arguments, 0);
25317         var ret = false;
25318         for(var i = 0; i < ar.length; i++) {
25319             if (!ar[i]) {
25320                 continue; // skip -- if this happends something invalid got sent, we 
25321                 // should ignore it, as basically that interface element will not show up
25322                 // and that should be pretty obvious!!
25323             }
25324             
25325             if (Roo.form[ar[i].xtype]) {
25326                 ar[i].form = this;
25327                 var fe = Roo.factory(ar[i], Roo.form);
25328                 if (!ret) {
25329                     ret = fe;
25330                 }
25331                 fe.form = this;
25332                 if (fe.store) {
25333                     fe.store.form = this;
25334                 }
25335                 if (fe.isLayout) {  
25336                          
25337                     this.start(fe);
25338                     this.allItems.push(fe);
25339                     if (fe.items && fe.addxtype) {
25340                         fe.addxtype.apply(fe, fe.items);
25341                         delete fe.items;
25342                     }
25343                      this.end();
25344                     continue;
25345                 }
25346                 
25347                 
25348                  
25349                 this.add(fe);
25350               //  console.log('adding ' + ar[i].xtype);
25351             }
25352             if (ar[i].xtype == 'Button') {  
25353                 //console.log('adding button');
25354                 //console.log(ar[i]);
25355                 this.addButton(ar[i]);
25356                 this.allItems.push(fe);
25357                 continue;
25358             }
25359             
25360             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
25361                 alert('end is not supported on xtype any more, use items');
25362             //    this.end();
25363             //    //console.log('adding end');
25364             }
25365             
25366         }
25367         return ret;
25368     },
25369     
25370     /**
25371      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
25372      * option "monitorValid"
25373      */
25374     startMonitoring : function(){
25375         if(!this.bound){
25376             this.bound = true;
25377             Roo.TaskMgr.start({
25378                 run : this.bindHandler,
25379                 interval : this.monitorPoll || 200,
25380                 scope: this
25381             });
25382         }
25383     },
25384
25385     /**
25386      * Stops monitoring of the valid state of this form
25387      */
25388     stopMonitoring : function(){
25389         this.bound = false;
25390     },
25391
25392     // private
25393     bindHandler : function(){
25394         if(!this.bound){
25395             return false; // stops binding
25396         }
25397         var valid = true;
25398         this.items.each(function(f){
25399             if(!f.isValid(true)){
25400                 valid = false;
25401                 return false;
25402             }
25403         });
25404         for(var i = 0, len = this.buttons.length; i < len; i++){
25405             var btn = this.buttons[i];
25406             if(btn.formBind === true && btn.disabled === valid){
25407                 btn.setDisabled(!valid);
25408             }
25409         }
25410         this.fireEvent('clientvalidation', this, valid);
25411     }
25412     
25413     
25414     
25415     
25416     
25417     
25418     
25419     
25420 });
25421
25422
25423 // back compat
25424 Roo.Form = Roo.form.Form;
25425 /*
25426  * Based on:
25427  * Ext JS Library 1.1.1
25428  * Copyright(c) 2006-2007, Ext JS, LLC.
25429  *
25430  * Originally Released Under LGPL - original licence link has changed is not relivant.
25431  *
25432  * Fork - LGPL
25433  * <script type="text/javascript">
25434  */
25435
25436 // as we use this in bootstrap.
25437 Roo.namespace('Roo.form');
25438  /**
25439  * @class Roo.form.Action
25440  * Internal Class used to handle form actions
25441  * @constructor
25442  * @param {Roo.form.BasicForm} el The form element or its id
25443  * @param {Object} config Configuration options
25444  */
25445
25446  
25447  
25448 // define the action interface
25449 Roo.form.Action = function(form, options){
25450     this.form = form;
25451     this.options = options || {};
25452 };
25453 /**
25454  * Client Validation Failed
25455  * @const 
25456  */
25457 Roo.form.Action.CLIENT_INVALID = 'client';
25458 /**
25459  * Server Validation Failed
25460  * @const 
25461  */
25462 Roo.form.Action.SERVER_INVALID = 'server';
25463  /**
25464  * Connect to Server Failed
25465  * @const 
25466  */
25467 Roo.form.Action.CONNECT_FAILURE = 'connect';
25468 /**
25469  * Reading Data from Server Failed
25470  * @const 
25471  */
25472 Roo.form.Action.LOAD_FAILURE = 'load';
25473
25474 Roo.form.Action.prototype = {
25475     type : 'default',
25476     failureType : undefined,
25477     response : undefined,
25478     result : undefined,
25479
25480     // interface method
25481     run : function(options){
25482
25483     },
25484
25485     // interface method
25486     success : function(response){
25487
25488     },
25489
25490     // interface method
25491     handleResponse : function(response){
25492
25493     },
25494
25495     // default connection failure
25496     failure : function(response){
25497         
25498         this.response = response;
25499         this.failureType = Roo.form.Action.CONNECT_FAILURE;
25500         this.form.afterAction(this, false);
25501     },
25502
25503     processResponse : function(response){
25504         this.response = response;
25505         if(!response.responseText){
25506             return true;
25507         }
25508         this.result = this.handleResponse(response);
25509         return this.result;
25510     },
25511
25512     // utility functions used internally
25513     getUrl : function(appendParams){
25514         var url = this.options.url || this.form.url || this.form.el.dom.action;
25515         if(appendParams){
25516             var p = this.getParams();
25517             if(p){
25518                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
25519             }
25520         }
25521         return url;
25522     },
25523
25524     getMethod : function(){
25525         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
25526     },
25527
25528     getParams : function(){
25529         var bp = this.form.baseParams;
25530         var p = this.options.params;
25531         if(p){
25532             if(typeof p == "object"){
25533                 p = Roo.urlEncode(Roo.applyIf(p, bp));
25534             }else if(typeof p == 'string' && bp){
25535                 p += '&' + Roo.urlEncode(bp);
25536             }
25537         }else if(bp){
25538             p = Roo.urlEncode(bp);
25539         }
25540         return p;
25541     },
25542
25543     createCallback : function(){
25544         return {
25545             success: this.success,
25546             failure: this.failure,
25547             scope: this,
25548             timeout: (this.form.timeout*1000),
25549             upload: this.form.fileUpload ? this.success : undefined
25550         };
25551     }
25552 };
25553
25554 Roo.form.Action.Submit = function(form, options){
25555     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
25556 };
25557
25558 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
25559     type : 'submit',
25560
25561     haveProgress : false,
25562     uploadComplete : false,
25563     
25564     // uploadProgress indicator.
25565     uploadProgress : function()
25566     {
25567         if (!this.form.progressUrl) {
25568             return;
25569         }
25570         
25571         if (!this.haveProgress) {
25572             Roo.MessageBox.progress("Uploading", "Uploading");
25573         }
25574         if (this.uploadComplete) {
25575            Roo.MessageBox.hide();
25576            return;
25577         }
25578         
25579         this.haveProgress = true;
25580    
25581         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
25582         
25583         var c = new Roo.data.Connection();
25584         c.request({
25585             url : this.form.progressUrl,
25586             params: {
25587                 id : uid
25588             },
25589             method: 'GET',
25590             success : function(req){
25591                //console.log(data);
25592                 var rdata = false;
25593                 var edata;
25594                 try  {
25595                    rdata = Roo.decode(req.responseText)
25596                 } catch (e) {
25597                     Roo.log("Invalid data from server..");
25598                     Roo.log(edata);
25599                     return;
25600                 }
25601                 if (!rdata || !rdata.success) {
25602                     Roo.log(rdata);
25603                     Roo.MessageBox.alert(Roo.encode(rdata));
25604                     return;
25605                 }
25606                 var data = rdata.data;
25607                 
25608                 if (this.uploadComplete) {
25609                    Roo.MessageBox.hide();
25610                    return;
25611                 }
25612                    
25613                 if (data){
25614                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
25615                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
25616                     );
25617                 }
25618                 this.uploadProgress.defer(2000,this);
25619             },
25620        
25621             failure: function(data) {
25622                 Roo.log('progress url failed ');
25623                 Roo.log(data);
25624             },
25625             scope : this
25626         });
25627            
25628     },
25629     
25630     
25631     run : function()
25632     {
25633         // run get Values on the form, so it syncs any secondary forms.
25634         this.form.getValues();
25635         
25636         var o = this.options;
25637         var method = this.getMethod();
25638         var isPost = method == 'POST';
25639         if(o.clientValidation === false || this.form.isValid()){
25640             
25641             if (this.form.progressUrl) {
25642                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
25643                     (new Date() * 1) + '' + Math.random());
25644                     
25645             } 
25646             
25647             
25648             Roo.Ajax.request(Roo.apply(this.createCallback(), {
25649                 form:this.form.el.dom,
25650                 url:this.getUrl(!isPost),
25651                 method: method,
25652                 params:isPost ? this.getParams() : null,
25653                 isUpload: this.form.fileUpload,
25654                 formData : this.form.formData
25655             }));
25656             
25657             this.uploadProgress();
25658
25659         }else if (o.clientValidation !== false){ // client validation failed
25660             this.failureType = Roo.form.Action.CLIENT_INVALID;
25661             this.form.afterAction(this, false);
25662         }
25663     },
25664
25665     success : function(response)
25666     {
25667         this.uploadComplete= true;
25668         if (this.haveProgress) {
25669             Roo.MessageBox.hide();
25670         }
25671         
25672         
25673         var result = this.processResponse(response);
25674         if(result === true || result.success){
25675             this.form.afterAction(this, true);
25676             return;
25677         }
25678         if(result.errors){
25679             this.form.markInvalid(result.errors);
25680             this.failureType = Roo.form.Action.SERVER_INVALID;
25681         }
25682         this.form.afterAction(this, false);
25683     },
25684     failure : function(response)
25685     {
25686         this.uploadComplete= true;
25687         if (this.haveProgress) {
25688             Roo.MessageBox.hide();
25689         }
25690         
25691         this.response = response;
25692         this.failureType = Roo.form.Action.CONNECT_FAILURE;
25693         this.form.afterAction(this, false);
25694     },
25695     
25696     handleResponse : function(response){
25697         if(this.form.errorReader){
25698             var rs = this.form.errorReader.read(response);
25699             var errors = [];
25700             if(rs.records){
25701                 for(var i = 0, len = rs.records.length; i < len; i++) {
25702                     var r = rs.records[i];
25703                     errors[i] = r.data;
25704                 }
25705             }
25706             if(errors.length < 1){
25707                 errors = null;
25708             }
25709             return {
25710                 success : rs.success,
25711                 errors : errors
25712             };
25713         }
25714         var ret = false;
25715         try {
25716             ret = Roo.decode(response.responseText);
25717         } catch (e) {
25718             ret = {
25719                 success: false,
25720                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
25721                 errors : []
25722             };
25723         }
25724         return ret;
25725         
25726     }
25727 });
25728
25729
25730 Roo.form.Action.Load = function(form, options){
25731     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
25732     this.reader = this.form.reader;
25733 };
25734
25735 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
25736     type : 'load',
25737
25738     run : function(){
25739         
25740         Roo.Ajax.request(Roo.apply(
25741                 this.createCallback(), {
25742                     method:this.getMethod(),
25743                     url:this.getUrl(false),
25744                     params:this.getParams()
25745         }));
25746     },
25747
25748     success : function(response){
25749         
25750         var result = this.processResponse(response);
25751         if(result === true || !result.success || !result.data){
25752             this.failureType = Roo.form.Action.LOAD_FAILURE;
25753             this.form.afterAction(this, false);
25754             return;
25755         }
25756         this.form.clearInvalid();
25757         this.form.setValues(result.data);
25758         this.form.afterAction(this, true);
25759     },
25760
25761     handleResponse : function(response){
25762         if(this.form.reader){
25763             var rs = this.form.reader.read(response);
25764             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
25765             return {
25766                 success : rs.success,
25767                 data : data
25768             };
25769         }
25770         return Roo.decode(response.responseText);
25771     }
25772 });
25773
25774 Roo.form.Action.ACTION_TYPES = {
25775     'load' : Roo.form.Action.Load,
25776     'submit' : Roo.form.Action.Submit
25777 };/*
25778  * Based on:
25779  * Ext JS Library 1.1.1
25780  * Copyright(c) 2006-2007, Ext JS, LLC.
25781  *
25782  * Originally Released Under LGPL - original licence link has changed is not relivant.
25783  *
25784  * Fork - LGPL
25785  * <script type="text/javascript">
25786  */
25787  
25788 /**
25789  * @class Roo.form.Layout
25790  * @extends Roo.Component
25791  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
25792  * @constructor
25793  * @param {Object} config Configuration options
25794  */
25795 Roo.form.Layout = function(config){
25796     var xitems = [];
25797     if (config.items) {
25798         xitems = config.items;
25799         delete config.items;
25800     }
25801     Roo.form.Layout.superclass.constructor.call(this, config);
25802     this.stack = [];
25803     Roo.each(xitems, this.addxtype, this);
25804      
25805 };
25806
25807 Roo.extend(Roo.form.Layout, Roo.Component, {
25808     /**
25809      * @cfg {String/Object} autoCreate
25810      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
25811      */
25812     /**
25813      * @cfg {String/Object/Function} style
25814      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
25815      * a function which returns such a specification.
25816      */
25817     /**
25818      * @cfg {String} labelAlign
25819      * Valid values are "left," "top" and "right" (defaults to "left")
25820      */
25821     /**
25822      * @cfg {Number} labelWidth
25823      * Fixed width in pixels of all field labels (defaults to undefined)
25824      */
25825     /**
25826      * @cfg {Boolean} clear
25827      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
25828      */
25829     clear : true,
25830     /**
25831      * @cfg {String} labelSeparator
25832      * The separator to use after field labels (defaults to ':')
25833      */
25834     labelSeparator : ':',
25835     /**
25836      * @cfg {Boolean} hideLabels
25837      * True to suppress the display of field labels in this layout (defaults to false)
25838      */
25839     hideLabels : false,
25840
25841     // private
25842     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
25843     
25844     isLayout : true,
25845     
25846     // private
25847     onRender : function(ct, position){
25848         if(this.el){ // from markup
25849             this.el = Roo.get(this.el);
25850         }else {  // generate
25851             var cfg = this.getAutoCreate();
25852             this.el = ct.createChild(cfg, position);
25853         }
25854         if(this.style){
25855             this.el.applyStyles(this.style);
25856         }
25857         if(this.labelAlign){
25858             this.el.addClass('x-form-label-'+this.labelAlign);
25859         }
25860         if(this.hideLabels){
25861             this.labelStyle = "display:none";
25862             this.elementStyle = "padding-left:0;";
25863         }else{
25864             if(typeof this.labelWidth == 'number'){
25865                 this.labelStyle = "width:"+this.labelWidth+"px;";
25866                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
25867             }
25868             if(this.labelAlign == 'top'){
25869                 this.labelStyle = "width:auto;";
25870                 this.elementStyle = "padding-left:0;";
25871             }
25872         }
25873         var stack = this.stack;
25874         var slen = stack.length;
25875         if(slen > 0){
25876             if(!this.fieldTpl){
25877                 var t = new Roo.Template(
25878                     '<div class="x-form-item {5}">',
25879                         '<label for="{0}" style="{2}">{1}{4}</label>',
25880                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
25881                         '</div>',
25882                     '</div><div class="x-form-clear-left"></div>'
25883                 );
25884                 t.disableFormats = true;
25885                 t.compile();
25886                 Roo.form.Layout.prototype.fieldTpl = t;
25887             }
25888             for(var i = 0; i < slen; i++) {
25889                 if(stack[i].isFormField){
25890                     this.renderField(stack[i]);
25891                 }else{
25892                     this.renderComponent(stack[i]);
25893                 }
25894             }
25895         }
25896         if(this.clear){
25897             this.el.createChild({cls:'x-form-clear'});
25898         }
25899     },
25900
25901     // private
25902     renderField : function(f){
25903         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
25904                f.id, //0
25905                f.fieldLabel, //1
25906                f.labelStyle||this.labelStyle||'', //2
25907                this.elementStyle||'', //3
25908                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
25909                f.itemCls||this.itemCls||''  //5
25910        ], true).getPrevSibling());
25911     },
25912
25913     // private
25914     renderComponent : function(c){
25915         c.render(c.isLayout ? this.el : this.el.createChild());    
25916     },
25917     /**
25918      * Adds a object form elements (using the xtype property as the factory method.)
25919      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
25920      * @param {Object} config 
25921      */
25922     addxtype : function(o)
25923     {
25924         // create the lement.
25925         o.form = this.form;
25926         var fe = Roo.factory(o, Roo.form);
25927         this.form.allItems.push(fe);
25928         this.stack.push(fe);
25929         
25930         if (fe.isFormField) {
25931             this.form.items.add(fe);
25932         }
25933          
25934         return fe;
25935     }
25936 });
25937
25938 /**
25939  * @class Roo.form.Column
25940  * @extends Roo.form.Layout
25941  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
25942  * @constructor
25943  * @param {Object} config Configuration options
25944  */
25945 Roo.form.Column = function(config){
25946     Roo.form.Column.superclass.constructor.call(this, config);
25947 };
25948
25949 Roo.extend(Roo.form.Column, Roo.form.Layout, {
25950     /**
25951      * @cfg {Number/String} width
25952      * The fixed width of the column in pixels or CSS value (defaults to "auto")
25953      */
25954     /**
25955      * @cfg {String/Object} autoCreate
25956      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
25957      */
25958
25959     // private
25960     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
25961
25962     // private
25963     onRender : function(ct, position){
25964         Roo.form.Column.superclass.onRender.call(this, ct, position);
25965         if(this.width){
25966             this.el.setWidth(this.width);
25967         }
25968     }
25969 });
25970
25971
25972 /**
25973  * @class Roo.form.Row
25974  * @extends Roo.form.Layout
25975  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
25976  * @constructor
25977  * @param {Object} config Configuration options
25978  */
25979
25980  
25981 Roo.form.Row = function(config){
25982     Roo.form.Row.superclass.constructor.call(this, config);
25983 };
25984  
25985 Roo.extend(Roo.form.Row, Roo.form.Layout, {
25986       /**
25987      * @cfg {Number/String} width
25988      * The fixed width of the column in pixels or CSS value (defaults to "auto")
25989      */
25990     /**
25991      * @cfg {Number/String} height
25992      * The fixed height of the column in pixels or CSS value (defaults to "auto")
25993      */
25994     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
25995     
25996     padWidth : 20,
25997     // private
25998     onRender : function(ct, position){
25999         //console.log('row render');
26000         if(!this.rowTpl){
26001             var t = new Roo.Template(
26002                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
26003                     '<label for="{0}" style="{2}">{1}{4}</label>',
26004                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
26005                     '</div>',
26006                 '</div>'
26007             );
26008             t.disableFormats = true;
26009             t.compile();
26010             Roo.form.Layout.prototype.rowTpl = t;
26011         }
26012         this.fieldTpl = this.rowTpl;
26013         
26014         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
26015         var labelWidth = 100;
26016         
26017         if ((this.labelAlign != 'top')) {
26018             if (typeof this.labelWidth == 'number') {
26019                 labelWidth = this.labelWidth
26020             }
26021             this.padWidth =  20 + labelWidth;
26022             
26023         }
26024         
26025         Roo.form.Column.superclass.onRender.call(this, ct, position);
26026         if(this.width){
26027             this.el.setWidth(this.width);
26028         }
26029         if(this.height){
26030             this.el.setHeight(this.height);
26031         }
26032     },
26033     
26034     // private
26035     renderField : function(f){
26036         f.fieldEl = this.fieldTpl.append(this.el, [
26037                f.id, f.fieldLabel,
26038                f.labelStyle||this.labelStyle||'',
26039                this.elementStyle||'',
26040                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
26041                f.itemCls||this.itemCls||'',
26042                f.width ? f.width + this.padWidth : 160 + this.padWidth
26043        ],true);
26044     }
26045 });
26046  
26047
26048 /**
26049  * @class Roo.form.FieldSet
26050  * @extends Roo.form.Layout
26051  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
26052  * @constructor
26053  * @param {Object} config Configuration options
26054  */
26055 Roo.form.FieldSet = function(config){
26056     Roo.form.FieldSet.superclass.constructor.call(this, config);
26057 };
26058
26059 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
26060     /**
26061      * @cfg {String} legend
26062      * The text to display as the legend for the FieldSet (defaults to '')
26063      */
26064     /**
26065      * @cfg {String/Object} autoCreate
26066      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
26067      */
26068
26069     // private
26070     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
26071
26072     // private
26073     onRender : function(ct, position){
26074         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
26075         if(this.legend){
26076             this.setLegend(this.legend);
26077         }
26078     },
26079
26080     // private
26081     setLegend : function(text){
26082         if(this.rendered){
26083             this.el.child('legend').update(text);
26084         }
26085     }
26086 });/*
26087  * Based on:
26088  * Ext JS Library 1.1.1
26089  * Copyright(c) 2006-2007, Ext JS, LLC.
26090  *
26091  * Originally Released Under LGPL - original licence link has changed is not relivant.
26092  *
26093  * Fork - LGPL
26094  * <script type="text/javascript">
26095  */
26096 /**
26097  * @class Roo.form.VTypes
26098  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
26099  * @singleton
26100  */
26101 Roo.form.VTypes = function(){
26102     // closure these in so they are only created once.
26103     var alpha = /^[a-zA-Z_]+$/;
26104     var alphanum = /^[a-zA-Z0-9_]+$/;
26105     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
26106     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
26107
26108     // All these messages and functions are configurable
26109     return {
26110         /**
26111          * The function used to validate email addresses
26112          * @param {String} value The email address
26113          */
26114         'email' : function(v){
26115             return email.test(v);
26116         },
26117         /**
26118          * The error text to display when the email validation function returns false
26119          * @type String
26120          */
26121         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
26122         /**
26123          * The keystroke filter mask to be applied on email input
26124          * @type RegExp
26125          */
26126         'emailMask' : /[a-z0-9_\.\-@]/i,
26127
26128         /**
26129          * The function used to validate URLs
26130          * @param {String} value The URL
26131          */
26132         'url' : function(v){
26133             return url.test(v);
26134         },
26135         /**
26136          * The error text to display when the url validation function returns false
26137          * @type String
26138          */
26139         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
26140         
26141         /**
26142          * The function used to validate alpha values
26143          * @param {String} value The value
26144          */
26145         'alpha' : function(v){
26146             return alpha.test(v);
26147         },
26148         /**
26149          * The error text to display when the alpha validation function returns false
26150          * @type String
26151          */
26152         'alphaText' : 'This field should only contain letters and _',
26153         /**
26154          * The keystroke filter mask to be applied on alpha input
26155          * @type RegExp
26156          */
26157         'alphaMask' : /[a-z_]/i,
26158
26159         /**
26160          * The function used to validate alphanumeric values
26161          * @param {String} value The value
26162          */
26163         'alphanum' : function(v){
26164             return alphanum.test(v);
26165         },
26166         /**
26167          * The error text to display when the alphanumeric validation function returns false
26168          * @type String
26169          */
26170         'alphanumText' : 'This field should only contain letters, numbers and _',
26171         /**
26172          * The keystroke filter mask to be applied on alphanumeric input
26173          * @type RegExp
26174          */
26175         'alphanumMask' : /[a-z0-9_]/i
26176     };
26177 }();//<script type="text/javascript">
26178
26179 /**
26180  * @class Roo.form.FCKeditor
26181  * @extends Roo.form.TextArea
26182  * Wrapper around the FCKEditor http://www.fckeditor.net
26183  * @constructor
26184  * Creates a new FCKeditor
26185  * @param {Object} config Configuration options
26186  */
26187 Roo.form.FCKeditor = function(config){
26188     Roo.form.FCKeditor.superclass.constructor.call(this, config);
26189     this.addEvents({
26190          /**
26191          * @event editorinit
26192          * Fired when the editor is initialized - you can add extra handlers here..
26193          * @param {FCKeditor} this
26194          * @param {Object} the FCK object.
26195          */
26196         editorinit : true
26197     });
26198     
26199     
26200 };
26201 Roo.form.FCKeditor.editors = { };
26202 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
26203 {
26204     //defaultAutoCreate : {
26205     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
26206     //},
26207     // private
26208     /**
26209      * @cfg {Object} fck options - see fck manual for details.
26210      */
26211     fckconfig : false,
26212     
26213     /**
26214      * @cfg {Object} fck toolbar set (Basic or Default)
26215      */
26216     toolbarSet : 'Basic',
26217     /**
26218      * @cfg {Object} fck BasePath
26219      */ 
26220     basePath : '/fckeditor/',
26221     
26222     
26223     frame : false,
26224     
26225     value : '',
26226     
26227    
26228     onRender : function(ct, position)
26229     {
26230         if(!this.el){
26231             this.defaultAutoCreate = {
26232                 tag: "textarea",
26233                 style:"width:300px;height:60px;",
26234                 autocomplete: "new-password"
26235             };
26236         }
26237         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
26238         /*
26239         if(this.grow){
26240             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
26241             if(this.preventScrollbars){
26242                 this.el.setStyle("overflow", "hidden");
26243             }
26244             this.el.setHeight(this.growMin);
26245         }
26246         */
26247         //console.log('onrender' + this.getId() );
26248         Roo.form.FCKeditor.editors[this.getId()] = this;
26249          
26250
26251         this.replaceTextarea() ;
26252         
26253     },
26254     
26255     getEditor : function() {
26256         return this.fckEditor;
26257     },
26258     /**
26259      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
26260      * @param {Mixed} value The value to set
26261      */
26262     
26263     
26264     setValue : function(value)
26265     {
26266         //console.log('setValue: ' + value);
26267         
26268         if(typeof(value) == 'undefined') { // not sure why this is happending...
26269             return;
26270         }
26271         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26272         
26273         //if(!this.el || !this.getEditor()) {
26274         //    this.value = value;
26275             //this.setValue.defer(100,this,[value]);    
26276         //    return;
26277         //} 
26278         
26279         if(!this.getEditor()) {
26280             return;
26281         }
26282         
26283         this.getEditor().SetData(value);
26284         
26285         //
26286
26287     },
26288
26289     /**
26290      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
26291      * @return {Mixed} value The field value
26292      */
26293     getValue : function()
26294     {
26295         
26296         if (this.frame && this.frame.dom.style.display == 'none') {
26297             return Roo.form.FCKeditor.superclass.getValue.call(this);
26298         }
26299         
26300         if(!this.el || !this.getEditor()) {
26301            
26302            // this.getValue.defer(100,this); 
26303             return this.value;
26304         }
26305        
26306         
26307         var value=this.getEditor().GetData();
26308         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26309         return Roo.form.FCKeditor.superclass.getValue.call(this);
26310         
26311
26312     },
26313
26314     /**
26315      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
26316      * @return {Mixed} value The field value
26317      */
26318     getRawValue : function()
26319     {
26320         if (this.frame && this.frame.dom.style.display == 'none') {
26321             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26322         }
26323         
26324         if(!this.el || !this.getEditor()) {
26325             //this.getRawValue.defer(100,this); 
26326             return this.value;
26327             return;
26328         }
26329         
26330         
26331         
26332         var value=this.getEditor().GetData();
26333         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
26334         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26335          
26336     },
26337     
26338     setSize : function(w,h) {
26339         
26340         
26341         
26342         //if (this.frame && this.frame.dom.style.display == 'none') {
26343         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26344         //    return;
26345         //}
26346         //if(!this.el || !this.getEditor()) {
26347         //    this.setSize.defer(100,this, [w,h]); 
26348         //    return;
26349         //}
26350         
26351         
26352         
26353         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26354         
26355         this.frame.dom.setAttribute('width', w);
26356         this.frame.dom.setAttribute('height', h);
26357         this.frame.setSize(w,h);
26358         
26359     },
26360     
26361     toggleSourceEdit : function(value) {
26362         
26363       
26364          
26365         this.el.dom.style.display = value ? '' : 'none';
26366         this.frame.dom.style.display = value ?  'none' : '';
26367         
26368     },
26369     
26370     
26371     focus: function(tag)
26372     {
26373         if (this.frame.dom.style.display == 'none') {
26374             return Roo.form.FCKeditor.superclass.focus.call(this);
26375         }
26376         if(!this.el || !this.getEditor()) {
26377             this.focus.defer(100,this, [tag]); 
26378             return;
26379         }
26380         
26381         
26382         
26383         
26384         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
26385         this.getEditor().Focus();
26386         if (tgs.length) {
26387             if (!this.getEditor().Selection.GetSelection()) {
26388                 this.focus.defer(100,this, [tag]); 
26389                 return;
26390             }
26391             
26392             
26393             var r = this.getEditor().EditorDocument.createRange();
26394             r.setStart(tgs[0],0);
26395             r.setEnd(tgs[0],0);
26396             this.getEditor().Selection.GetSelection().removeAllRanges();
26397             this.getEditor().Selection.GetSelection().addRange(r);
26398             this.getEditor().Focus();
26399         }
26400         
26401     },
26402     
26403     
26404     
26405     replaceTextarea : function()
26406     {
26407         if ( document.getElementById( this.getId() + '___Frame' ) ) {
26408             return ;
26409         }
26410         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
26411         //{
26412             // We must check the elements firstly using the Id and then the name.
26413         var oTextarea = document.getElementById( this.getId() );
26414         
26415         var colElementsByName = document.getElementsByName( this.getId() ) ;
26416          
26417         oTextarea.style.display = 'none' ;
26418
26419         if ( oTextarea.tabIndex ) {            
26420             this.TabIndex = oTextarea.tabIndex ;
26421         }
26422         
26423         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
26424         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
26425         this.frame = Roo.get(this.getId() + '___Frame')
26426     },
26427     
26428     _getConfigHtml : function()
26429     {
26430         var sConfig = '' ;
26431
26432         for ( var o in this.fckconfig ) {
26433             sConfig += sConfig.length > 0  ? '&amp;' : '';
26434             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
26435         }
26436
26437         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
26438     },
26439     
26440     
26441     _getIFrameHtml : function()
26442     {
26443         var sFile = 'fckeditor.html' ;
26444         /* no idea what this is about..
26445         try
26446         {
26447             if ( (/fcksource=true/i).test( window.top.location.search ) )
26448                 sFile = 'fckeditor.original.html' ;
26449         }
26450         catch (e) { 
26451         */
26452
26453         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
26454         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
26455         
26456         
26457         var html = '<iframe id="' + this.getId() +
26458             '___Frame" src="' + sLink +
26459             '" width="' + this.width +
26460             '" height="' + this.height + '"' +
26461             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
26462             ' frameborder="0" scrolling="no"></iframe>' ;
26463
26464         return html ;
26465     },
26466     
26467     _insertHtmlBefore : function( html, element )
26468     {
26469         if ( element.insertAdjacentHTML )       {
26470             // IE
26471             element.insertAdjacentHTML( 'beforeBegin', html ) ;
26472         } else { // Gecko
26473             var oRange = document.createRange() ;
26474             oRange.setStartBefore( element ) ;
26475             var oFragment = oRange.createContextualFragment( html );
26476             element.parentNode.insertBefore( oFragment, element ) ;
26477         }
26478     }
26479     
26480     
26481   
26482     
26483     
26484     
26485     
26486
26487 });
26488
26489 //Roo.reg('fckeditor', Roo.form.FCKeditor);
26490
26491 function FCKeditor_OnComplete(editorInstance){
26492     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
26493     f.fckEditor = editorInstance;
26494     //console.log("loaded");
26495     f.fireEvent('editorinit', f, editorInstance);
26496
26497   
26498
26499  
26500
26501
26502
26503
26504
26505
26506
26507
26508
26509
26510
26511
26512
26513
26514
26515 //<script type="text/javascript">
26516 /**
26517  * @class Roo.form.GridField
26518  * @extends Roo.form.Field
26519  * Embed a grid (or editable grid into a form)
26520  * STATUS ALPHA
26521  * 
26522  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
26523  * it needs 
26524  * xgrid.store = Roo.data.Store
26525  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
26526  * xgrid.store.reader = Roo.data.JsonReader 
26527  * 
26528  * 
26529  * @constructor
26530  * Creates a new GridField
26531  * @param {Object} config Configuration options
26532  */
26533 Roo.form.GridField = function(config){
26534     Roo.form.GridField.superclass.constructor.call(this, config);
26535      
26536 };
26537
26538 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
26539     /**
26540      * @cfg {Number} width  - used to restrict width of grid..
26541      */
26542     width : 100,
26543     /**
26544      * @cfg {Number} height - used to restrict height of grid..
26545      */
26546     height : 50,
26547      /**
26548      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
26549          * 
26550          *}
26551      */
26552     xgrid : false, 
26553     /**
26554      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
26555      * {tag: "input", type: "checkbox", autocomplete: "off"})
26556      */
26557    // defaultAutoCreate : { tag: 'div' },
26558     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
26559     /**
26560      * @cfg {String} addTitle Text to include for adding a title.
26561      */
26562     addTitle : false,
26563     //
26564     onResize : function(){
26565         Roo.form.Field.superclass.onResize.apply(this, arguments);
26566     },
26567
26568     initEvents : function(){
26569         // Roo.form.Checkbox.superclass.initEvents.call(this);
26570         // has no events...
26571        
26572     },
26573
26574
26575     getResizeEl : function(){
26576         return this.wrap;
26577     },
26578
26579     getPositionEl : function(){
26580         return this.wrap;
26581     },
26582
26583     // private
26584     onRender : function(ct, position){
26585         
26586         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
26587         var style = this.style;
26588         delete this.style;
26589         
26590         Roo.form.GridField.superclass.onRender.call(this, ct, position);
26591         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
26592         this.viewEl = this.wrap.createChild({ tag: 'div' });
26593         if (style) {
26594             this.viewEl.applyStyles(style);
26595         }
26596         if (this.width) {
26597             this.viewEl.setWidth(this.width);
26598         }
26599         if (this.height) {
26600             this.viewEl.setHeight(this.height);
26601         }
26602         //if(this.inputValue !== undefined){
26603         //this.setValue(this.value);
26604         
26605         
26606         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
26607         
26608         
26609         this.grid.render();
26610         this.grid.getDataSource().on('remove', this.refreshValue, this);
26611         this.grid.getDataSource().on('update', this.refreshValue, this);
26612         this.grid.on('afteredit', this.refreshValue, this);
26613  
26614     },
26615      
26616     
26617     /**
26618      * Sets the value of the item. 
26619      * @param {String} either an object  or a string..
26620      */
26621     setValue : function(v){
26622         //this.value = v;
26623         v = v || []; // empty set..
26624         // this does not seem smart - it really only affects memoryproxy grids..
26625         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
26626             var ds = this.grid.getDataSource();
26627             // assumes a json reader..
26628             var data = {}
26629             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
26630             ds.loadData( data);
26631         }
26632         // clear selection so it does not get stale.
26633         if (this.grid.sm) { 
26634             this.grid.sm.clearSelections();
26635         }
26636         
26637         Roo.form.GridField.superclass.setValue.call(this, v);
26638         this.refreshValue();
26639         // should load data in the grid really....
26640     },
26641     
26642     // private
26643     refreshValue: function() {
26644          var val = [];
26645         this.grid.getDataSource().each(function(r) {
26646             val.push(r.data);
26647         });
26648         this.el.dom.value = Roo.encode(val);
26649     }
26650     
26651      
26652     
26653     
26654 });/*
26655  * Based on:
26656  * Ext JS Library 1.1.1
26657  * Copyright(c) 2006-2007, Ext JS, LLC.
26658  *
26659  * Originally Released Under LGPL - original licence link has changed is not relivant.
26660  *
26661  * Fork - LGPL
26662  * <script type="text/javascript">
26663  */
26664 /**
26665  * @class Roo.form.DisplayField
26666  * @extends Roo.form.Field
26667  * A generic Field to display non-editable data.
26668  * @cfg {Boolean} closable (true|false) default false
26669  * @constructor
26670  * Creates a new Display Field item.
26671  * @param {Object} config Configuration options
26672  */
26673 Roo.form.DisplayField = function(config){
26674     Roo.form.DisplayField.superclass.constructor.call(this, config);
26675     
26676     this.addEvents({
26677         /**
26678          * @event close
26679          * Fires after the click the close btn
26680              * @param {Roo.form.DisplayField} this
26681              */
26682         close : true
26683     });
26684 };
26685
26686 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
26687     inputType:      'hidden',
26688     allowBlank:     true,
26689     readOnly:         true,
26690     
26691  
26692     /**
26693      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
26694      */
26695     focusClass : undefined,
26696     /**
26697      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
26698      */
26699     fieldClass: 'x-form-field',
26700     
26701      /**
26702      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
26703      */
26704     valueRenderer: undefined,
26705     
26706     width: 100,
26707     /**
26708      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
26709      * {tag: "input", type: "checkbox", autocomplete: "off"})
26710      */
26711      
26712  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
26713  
26714     closable : false,
26715     
26716     onResize : function(){
26717         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
26718         
26719     },
26720
26721     initEvents : function(){
26722         // Roo.form.Checkbox.superclass.initEvents.call(this);
26723         // has no events...
26724         
26725         if(this.closable){
26726             this.closeEl.on('click', this.onClose, this);
26727         }
26728        
26729     },
26730
26731
26732     getResizeEl : function(){
26733         return this.wrap;
26734     },
26735
26736     getPositionEl : function(){
26737         return this.wrap;
26738     },
26739
26740     // private
26741     onRender : function(ct, position){
26742         
26743         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
26744         //if(this.inputValue !== undefined){
26745         this.wrap = this.el.wrap();
26746         
26747         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
26748         
26749         if(this.closable){
26750             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
26751         }
26752         
26753         if (this.bodyStyle) {
26754             this.viewEl.applyStyles(this.bodyStyle);
26755         }
26756         //this.viewEl.setStyle('padding', '2px');
26757         
26758         this.setValue(this.value);
26759         
26760     },
26761 /*
26762     // private
26763     initValue : Roo.emptyFn,
26764
26765   */
26766
26767         // private
26768     onClick : function(){
26769         
26770     },
26771
26772     /**
26773      * Sets the checked state of the checkbox.
26774      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
26775      */
26776     setValue : function(v){
26777         this.value = v;
26778         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
26779         // this might be called before we have a dom element..
26780         if (!this.viewEl) {
26781             return;
26782         }
26783         this.viewEl.dom.innerHTML = html;
26784         Roo.form.DisplayField.superclass.setValue.call(this, v);
26785
26786     },
26787     
26788     onClose : function(e)
26789     {
26790         e.preventDefault();
26791         
26792         this.fireEvent('close', this);
26793     }
26794 });/*
26795  * 
26796  * Licence- LGPL
26797  * 
26798  */
26799
26800 /**
26801  * @class Roo.form.DayPicker
26802  * @extends Roo.form.Field
26803  * A Day picker show [M] [T] [W] ....
26804  * @constructor
26805  * Creates a new Day Picker
26806  * @param {Object} config Configuration options
26807  */
26808 Roo.form.DayPicker= function(config){
26809     Roo.form.DayPicker.superclass.constructor.call(this, config);
26810      
26811 };
26812
26813 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
26814     /**
26815      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
26816      */
26817     focusClass : undefined,
26818     /**
26819      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
26820      */
26821     fieldClass: "x-form-field",
26822    
26823     /**
26824      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
26825      * {tag: "input", type: "checkbox", autocomplete: "off"})
26826      */
26827     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
26828     
26829    
26830     actionMode : 'viewEl', 
26831     //
26832     // private
26833  
26834     inputType : 'hidden',
26835     
26836      
26837     inputElement: false, // real input element?
26838     basedOn: false, // ????
26839     
26840     isFormField: true, // not sure where this is needed!!!!
26841
26842     onResize : function(){
26843         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
26844         if(!this.boxLabel){
26845             this.el.alignTo(this.wrap, 'c-c');
26846         }
26847     },
26848
26849     initEvents : function(){
26850         Roo.form.Checkbox.superclass.initEvents.call(this);
26851         this.el.on("click", this.onClick,  this);
26852         this.el.on("change", this.onClick,  this);
26853     },
26854
26855
26856     getResizeEl : function(){
26857         return this.wrap;
26858     },
26859
26860     getPositionEl : function(){
26861         return this.wrap;
26862     },
26863
26864     
26865     // private
26866     onRender : function(ct, position){
26867         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
26868        
26869         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
26870         
26871         var r1 = '<table><tr>';
26872         var r2 = '<tr class="x-form-daypick-icons">';
26873         for (var i=0; i < 7; i++) {
26874             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
26875             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
26876         }
26877         
26878         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
26879         viewEl.select('img').on('click', this.onClick, this);
26880         this.viewEl = viewEl;   
26881         
26882         
26883         // this will not work on Chrome!!!
26884         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
26885         this.el.on('propertychange', this.setFromHidden,  this);  //ie
26886         
26887         
26888           
26889
26890     },
26891
26892     // private
26893     initValue : Roo.emptyFn,
26894
26895     /**
26896      * Returns the checked state of the checkbox.
26897      * @return {Boolean} True if checked, else false
26898      */
26899     getValue : function(){
26900         return this.el.dom.value;
26901         
26902     },
26903
26904         // private
26905     onClick : function(e){ 
26906         //this.setChecked(!this.checked);
26907         Roo.get(e.target).toggleClass('x-menu-item-checked');
26908         this.refreshValue();
26909         //if(this.el.dom.checked != this.checked){
26910         //    this.setValue(this.el.dom.checked);
26911        // }
26912     },
26913     
26914     // private
26915     refreshValue : function()
26916     {
26917         var val = '';
26918         this.viewEl.select('img',true).each(function(e,i,n)  {
26919             val += e.is(".x-menu-item-checked") ? String(n) : '';
26920         });
26921         this.setValue(val, true);
26922     },
26923
26924     /**
26925      * Sets the checked state of the checkbox.
26926      * On is always based on a string comparison between inputValue and the param.
26927      * @param {Boolean/String} value - the value to set 
26928      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
26929      */
26930     setValue : function(v,suppressEvent){
26931         if (!this.el.dom) {
26932             return;
26933         }
26934         var old = this.el.dom.value ;
26935         this.el.dom.value = v;
26936         if (suppressEvent) {
26937             return ;
26938         }
26939          
26940         // update display..
26941         this.viewEl.select('img',true).each(function(e,i,n)  {
26942             
26943             var on = e.is(".x-menu-item-checked");
26944             var newv = v.indexOf(String(n)) > -1;
26945             if (on != newv) {
26946                 e.toggleClass('x-menu-item-checked');
26947             }
26948             
26949         });
26950         
26951         
26952         this.fireEvent('change', this, v, old);
26953         
26954         
26955     },
26956    
26957     // handle setting of hidden value by some other method!!?!?
26958     setFromHidden: function()
26959     {
26960         if(!this.el){
26961             return;
26962         }
26963         //console.log("SET FROM HIDDEN");
26964         //alert('setFrom hidden');
26965         this.setValue(this.el.dom.value);
26966     },
26967     
26968     onDestroy : function()
26969     {
26970         if(this.viewEl){
26971             Roo.get(this.viewEl).remove();
26972         }
26973          
26974         Roo.form.DayPicker.superclass.onDestroy.call(this);
26975     }
26976
26977 });/*
26978  * RooJS Library 1.1.1
26979  * Copyright(c) 2008-2011  Alan Knowles
26980  *
26981  * License - LGPL
26982  */
26983  
26984
26985 /**
26986  * @class Roo.form.ComboCheck
26987  * @extends Roo.form.ComboBox
26988  * A combobox for multiple select items.
26989  *
26990  * FIXME - could do with a reset button..
26991  * 
26992  * @constructor
26993  * Create a new ComboCheck
26994  * @param {Object} config Configuration options
26995  */
26996 Roo.form.ComboCheck = function(config){
26997     Roo.form.ComboCheck.superclass.constructor.call(this, config);
26998     // should verify some data...
26999     // like
27000     // hiddenName = required..
27001     // displayField = required
27002     // valudField == required
27003     var req= [ 'hiddenName', 'displayField', 'valueField' ];
27004     var _t = this;
27005     Roo.each(req, function(e) {
27006         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
27007             throw "Roo.form.ComboCheck : missing value for: " + e;
27008         }
27009     });
27010     
27011     
27012 };
27013
27014 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
27015      
27016      
27017     editable : false,
27018      
27019     selectedClass: 'x-menu-item-checked', 
27020     
27021     // private
27022     onRender : function(ct, position){
27023         var _t = this;
27024         
27025         
27026         
27027         if(!this.tpl){
27028             var cls = 'x-combo-list';
27029
27030             
27031             this.tpl =  new Roo.Template({
27032                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
27033                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
27034                    '<span>{' + this.displayField + '}</span>' +
27035                     '</div>' 
27036                 
27037             });
27038         }
27039  
27040         
27041         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
27042         this.view.singleSelect = false;
27043         this.view.multiSelect = true;
27044         this.view.toggleSelect = true;
27045         this.pageTb.add(new Roo.Toolbar.Fill(), {
27046             
27047             text: 'Done',
27048             handler: function()
27049             {
27050                 _t.collapse();
27051             }
27052         });
27053     },
27054     
27055     onViewOver : function(e, t){
27056         // do nothing...
27057         return;
27058         
27059     },
27060     
27061     onViewClick : function(doFocus,index){
27062         return;
27063         
27064     },
27065     select: function () {
27066         //Roo.log("SELECT CALLED");
27067     },
27068      
27069     selectByValue : function(xv, scrollIntoView){
27070         var ar = this.getValueArray();
27071         var sels = [];
27072         
27073         Roo.each(ar, function(v) {
27074             if(v === undefined || v === null){
27075                 return;
27076             }
27077             var r = this.findRecord(this.valueField, v);
27078             if(r){
27079                 sels.push(this.store.indexOf(r))
27080                 
27081             }
27082         },this);
27083         this.view.select(sels);
27084         return false;
27085     },
27086     
27087     
27088     
27089     onSelect : function(record, index){
27090        // Roo.log("onselect Called");
27091        // this is only called by the clear button now..
27092         this.view.clearSelections();
27093         this.setValue('[]');
27094         if (this.value != this.valueBefore) {
27095             this.fireEvent('change', this, this.value, this.valueBefore);
27096             this.valueBefore = this.value;
27097         }
27098     },
27099     getValueArray : function()
27100     {
27101         var ar = [] ;
27102         
27103         try {
27104             //Roo.log(this.value);
27105             if (typeof(this.value) == 'undefined') {
27106                 return [];
27107             }
27108             var ar = Roo.decode(this.value);
27109             return  ar instanceof Array ? ar : []; //?? valid?
27110             
27111         } catch(e) {
27112             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
27113             return [];
27114         }
27115          
27116     },
27117     expand : function ()
27118     {
27119         
27120         Roo.form.ComboCheck.superclass.expand.call(this);
27121         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
27122         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
27123         
27124
27125     },
27126     
27127     collapse : function(){
27128         Roo.form.ComboCheck.superclass.collapse.call(this);
27129         var sl = this.view.getSelectedIndexes();
27130         var st = this.store;
27131         var nv = [];
27132         var tv = [];
27133         var r;
27134         Roo.each(sl, function(i) {
27135             r = st.getAt(i);
27136             nv.push(r.get(this.valueField));
27137         },this);
27138         this.setValue(Roo.encode(nv));
27139         if (this.value != this.valueBefore) {
27140
27141             this.fireEvent('change', this, this.value, this.valueBefore);
27142             this.valueBefore = this.value;
27143         }
27144         
27145     },
27146     
27147     setValue : function(v){
27148         // Roo.log(v);
27149         this.value = v;
27150         
27151         var vals = this.getValueArray();
27152         var tv = [];
27153         Roo.each(vals, function(k) {
27154             var r = this.findRecord(this.valueField, k);
27155             if(r){
27156                 tv.push(r.data[this.displayField]);
27157             }else if(this.valueNotFoundText !== undefined){
27158                 tv.push( this.valueNotFoundText );
27159             }
27160         },this);
27161        // Roo.log(tv);
27162         
27163         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
27164         this.hiddenField.value = v;
27165         this.value = v;
27166     }
27167     
27168 });/*
27169  * Based on:
27170  * Ext JS Library 1.1.1
27171  * Copyright(c) 2006-2007, Ext JS, LLC.
27172  *
27173  * Originally Released Under LGPL - original licence link has changed is not relivant.
27174  *
27175  * Fork - LGPL
27176  * <script type="text/javascript">
27177  */
27178  
27179 /**
27180  * @class Roo.form.Signature
27181  * @extends Roo.form.Field
27182  * Signature field.  
27183  * @constructor
27184  * 
27185  * @param {Object} config Configuration options
27186  */
27187
27188 Roo.form.Signature = function(config){
27189     Roo.form.Signature.superclass.constructor.call(this, config);
27190     
27191     this.addEvents({// not in used??
27192          /**
27193          * @event confirm
27194          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
27195              * @param {Roo.form.Signature} combo This combo box
27196              */
27197         'confirm' : true,
27198         /**
27199          * @event reset
27200          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
27201              * @param {Roo.form.ComboBox} combo This combo box
27202              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
27203              */
27204         'reset' : true
27205     });
27206 };
27207
27208 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
27209     /**
27210      * @cfg {Object} labels Label to use when rendering a form.
27211      * defaults to 
27212      * labels : { 
27213      *      clear : "Clear",
27214      *      confirm : "Confirm"
27215      *  }
27216      */
27217     labels : { 
27218         clear : "Clear",
27219         confirm : "Confirm"
27220     },
27221     /**
27222      * @cfg {Number} width The signature panel width (defaults to 300)
27223      */
27224     width: 300,
27225     /**
27226      * @cfg {Number} height The signature panel height (defaults to 100)
27227      */
27228     height : 100,
27229     /**
27230      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
27231      */
27232     allowBlank : false,
27233     
27234     //private
27235     // {Object} signPanel The signature SVG panel element (defaults to {})
27236     signPanel : {},
27237     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
27238     isMouseDown : false,
27239     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
27240     isConfirmed : false,
27241     // {String} signatureTmp SVG mapping string (defaults to empty string)
27242     signatureTmp : '',
27243     
27244     
27245     defaultAutoCreate : { // modified by initCompnoent..
27246         tag: "input",
27247         type:"hidden"
27248     },
27249
27250     // private
27251     onRender : function(ct, position){
27252         
27253         Roo.form.Signature.superclass.onRender.call(this, ct, position);
27254         
27255         this.wrap = this.el.wrap({
27256             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
27257         });
27258         
27259         this.createToolbar(this);
27260         this.signPanel = this.wrap.createChild({
27261                 tag: 'div',
27262                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
27263             }, this.el
27264         );
27265             
27266         this.svgID = Roo.id();
27267         this.svgEl = this.signPanel.createChild({
27268               xmlns : 'http://www.w3.org/2000/svg',
27269               tag : 'svg',
27270               id : this.svgID + "-svg",
27271               width: this.width,
27272               height: this.height,
27273               viewBox: '0 0 '+this.width+' '+this.height,
27274               cn : [
27275                 {
27276                     tag: "rect",
27277                     id: this.svgID + "-svg-r",
27278                     width: this.width,
27279                     height: this.height,
27280                     fill: "#ffa"
27281                 },
27282                 {
27283                     tag: "line",
27284                     id: this.svgID + "-svg-l",
27285                     x1: "0", // start
27286                     y1: (this.height*0.8), // start set the line in 80% of height
27287                     x2: this.width, // end
27288                     y2: (this.height*0.8), // end set the line in 80% of height
27289                     'stroke': "#666",
27290                     'stroke-width': "1",
27291                     'stroke-dasharray': "3",
27292                     'shape-rendering': "crispEdges",
27293                     'pointer-events': "none"
27294                 },
27295                 {
27296                     tag: "path",
27297                     id: this.svgID + "-svg-p",
27298                     'stroke': "navy",
27299                     'stroke-width': "3",
27300                     'fill': "none",
27301                     'pointer-events': 'none'
27302                 }
27303               ]
27304         });
27305         this.createSVG();
27306         this.svgBox = this.svgEl.dom.getScreenCTM();
27307     },
27308     createSVG : function(){ 
27309         var svg = this.signPanel;
27310         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
27311         var t = this;
27312
27313         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
27314         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
27315         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
27316         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
27317         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
27318         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
27319         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
27320         
27321     },
27322     isTouchEvent : function(e){
27323         return e.type.match(/^touch/);
27324     },
27325     getCoords : function (e) {
27326         var pt    = this.svgEl.dom.createSVGPoint();
27327         pt.x = e.clientX; 
27328         pt.y = e.clientY;
27329         if (this.isTouchEvent(e)) {
27330             pt.x =  e.targetTouches[0].clientX;
27331             pt.y = e.targetTouches[0].clientY;
27332         }
27333         var a = this.svgEl.dom.getScreenCTM();
27334         var b = a.inverse();
27335         var mx = pt.matrixTransform(b);
27336         return mx.x + ',' + mx.y;
27337     },
27338     //mouse event headler 
27339     down : function (e) {
27340         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
27341         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
27342         
27343         this.isMouseDown = true;
27344         
27345         e.preventDefault();
27346     },
27347     move : function (e) {
27348         if (this.isMouseDown) {
27349             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
27350             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
27351         }
27352         
27353         e.preventDefault();
27354     },
27355     up : function (e) {
27356         this.isMouseDown = false;
27357         var sp = this.signatureTmp.split(' ');
27358         
27359         if(sp.length > 1){
27360             if(!sp[sp.length-2].match(/^L/)){
27361                 sp.pop();
27362                 sp.pop();
27363                 sp.push("");
27364                 this.signatureTmp = sp.join(" ");
27365             }
27366         }
27367         if(this.getValue() != this.signatureTmp){
27368             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27369             this.isConfirmed = false;
27370         }
27371         e.preventDefault();
27372     },
27373     
27374     /**
27375      * Protected method that will not generally be called directly. It
27376      * is called when the editor creates its toolbar. Override this method if you need to
27377      * add custom toolbar buttons.
27378      * @param {HtmlEditor} editor
27379      */
27380     createToolbar : function(editor){
27381          function btn(id, toggle, handler){
27382             var xid = fid + '-'+ id ;
27383             return {
27384                 id : xid,
27385                 cmd : id,
27386                 cls : 'x-btn-icon x-edit-'+id,
27387                 enableToggle:toggle !== false,
27388                 scope: editor, // was editor...
27389                 handler:handler||editor.relayBtnCmd,
27390                 clickEvent:'mousedown',
27391                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27392                 tabIndex:-1
27393             };
27394         }
27395         
27396         
27397         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
27398         this.tb = tb;
27399         this.tb.add(
27400            {
27401                 cls : ' x-signature-btn x-signature-'+id,
27402                 scope: editor, // was editor...
27403                 handler: this.reset,
27404                 clickEvent:'mousedown',
27405                 text: this.labels.clear
27406             },
27407             {
27408                  xtype : 'Fill',
27409                  xns: Roo.Toolbar
27410             }, 
27411             {
27412                 cls : '  x-signature-btn x-signature-'+id,
27413                 scope: editor, // was editor...
27414                 handler: this.confirmHandler,
27415                 clickEvent:'mousedown',
27416                 text: this.labels.confirm
27417             }
27418         );
27419     
27420     },
27421     //public
27422     /**
27423      * when user is clicked confirm then show this image.....
27424      * 
27425      * @return {String} Image Data URI
27426      */
27427     getImageDataURI : function(){
27428         var svg = this.svgEl.dom.parentNode.innerHTML;
27429         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
27430         return src; 
27431     },
27432     /**
27433      * 
27434      * @return {Boolean} this.isConfirmed
27435      */
27436     getConfirmed : function(){
27437         return this.isConfirmed;
27438     },
27439     /**
27440      * 
27441      * @return {Number} this.width
27442      */
27443     getWidth : function(){
27444         return this.width;
27445     },
27446     /**
27447      * 
27448      * @return {Number} this.height
27449      */
27450     getHeight : function(){
27451         return this.height;
27452     },
27453     // private
27454     getSignature : function(){
27455         return this.signatureTmp;
27456     },
27457     // private
27458     reset : function(){
27459         this.signatureTmp = '';
27460         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27461         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
27462         this.isConfirmed = false;
27463         Roo.form.Signature.superclass.reset.call(this);
27464     },
27465     setSignature : function(s){
27466         this.signatureTmp = s;
27467         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27468         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
27469         this.setValue(s);
27470         this.isConfirmed = false;
27471         Roo.form.Signature.superclass.reset.call(this);
27472     }, 
27473     test : function(){
27474 //        Roo.log(this.signPanel.dom.contentWindow.up())
27475     },
27476     //private
27477     setConfirmed : function(){
27478         
27479         
27480         
27481 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
27482     },
27483     // private
27484     confirmHandler : function(){
27485         if(!this.getSignature()){
27486             return;
27487         }
27488         
27489         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
27490         this.setValue(this.getSignature());
27491         this.isConfirmed = true;
27492         
27493         this.fireEvent('confirm', this);
27494     },
27495     // private
27496     // Subclasses should provide the validation implementation by overriding this
27497     validateValue : function(value){
27498         if(this.allowBlank){
27499             return true;
27500         }
27501         
27502         if(this.isConfirmed){
27503             return true;
27504         }
27505         return false;
27506     }
27507 });/*
27508  * Based on:
27509  * Ext JS Library 1.1.1
27510  * Copyright(c) 2006-2007, Ext JS, LLC.
27511  *
27512  * Originally Released Under LGPL - original licence link has changed is not relivant.
27513  *
27514  * Fork - LGPL
27515  * <script type="text/javascript">
27516  */
27517  
27518
27519 /**
27520  * @class Roo.form.ComboBox
27521  * @extends Roo.form.TriggerField
27522  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
27523  * @constructor
27524  * Create a new ComboBox.
27525  * @param {Object} config Configuration options
27526  */
27527 Roo.form.Select = function(config){
27528     Roo.form.Select.superclass.constructor.call(this, config);
27529      
27530 };
27531
27532 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
27533     /**
27534      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
27535      */
27536     /**
27537      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
27538      * rendering into an Roo.Editor, defaults to false)
27539      */
27540     /**
27541      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
27542      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
27543      */
27544     /**
27545      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
27546      */
27547     /**
27548      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
27549      * the dropdown list (defaults to undefined, with no header element)
27550      */
27551
27552      /**
27553      * @cfg {String/Roo.Template} tpl The template to use to render the output
27554      */
27555      
27556     // private
27557     defaultAutoCreate : {tag: "select"  },
27558     /**
27559      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
27560      */
27561     listWidth: undefined,
27562     /**
27563      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
27564      * mode = 'remote' or 'text' if mode = 'local')
27565      */
27566     displayField: undefined,
27567     /**
27568      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
27569      * mode = 'remote' or 'value' if mode = 'local'). 
27570      * Note: use of a valueField requires the user make a selection
27571      * in order for a value to be mapped.
27572      */
27573     valueField: undefined,
27574     
27575     
27576     /**
27577      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
27578      * field's data value (defaults to the underlying DOM element's name)
27579      */
27580     hiddenName: undefined,
27581     /**
27582      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
27583      */
27584     listClass: '',
27585     /**
27586      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
27587      */
27588     selectedClass: 'x-combo-selected',
27589     /**
27590      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
27591      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
27592      * which displays a downward arrow icon).
27593      */
27594     triggerClass : 'x-form-arrow-trigger',
27595     /**
27596      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
27597      */
27598     shadow:'sides',
27599     /**
27600      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
27601      * anchor positions (defaults to 'tl-bl')
27602      */
27603     listAlign: 'tl-bl?',
27604     /**
27605      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
27606      */
27607     maxHeight: 300,
27608     /**
27609      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
27610      * query specified by the allQuery config option (defaults to 'query')
27611      */
27612     triggerAction: 'query',
27613     /**
27614      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
27615      * (defaults to 4, does not apply if editable = false)
27616      */
27617     minChars : 4,
27618     /**
27619      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
27620      * delay (typeAheadDelay) if it matches a known value (defaults to false)
27621      */
27622     typeAhead: false,
27623     /**
27624      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
27625      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
27626      */
27627     queryDelay: 500,
27628     /**
27629      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
27630      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
27631      */
27632     pageSize: 0,
27633     /**
27634      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
27635      * when editable = true (defaults to false)
27636      */
27637     selectOnFocus:false,
27638     /**
27639      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
27640      */
27641     queryParam: 'query',
27642     /**
27643      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
27644      * when mode = 'remote' (defaults to 'Loading...')
27645      */
27646     loadingText: 'Loading...',
27647     /**
27648      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
27649      */
27650     resizable: false,
27651     /**
27652      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
27653      */
27654     handleHeight : 8,
27655     /**
27656      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
27657      * traditional select (defaults to true)
27658      */
27659     editable: true,
27660     /**
27661      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
27662      */
27663     allQuery: '',
27664     /**
27665      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
27666      */
27667     mode: 'remote',
27668     /**
27669      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
27670      * listWidth has a higher value)
27671      */
27672     minListWidth : 70,
27673     /**
27674      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
27675      * allow the user to set arbitrary text into the field (defaults to false)
27676      */
27677     forceSelection:false,
27678     /**
27679      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
27680      * if typeAhead = true (defaults to 250)
27681      */
27682     typeAheadDelay : 250,
27683     /**
27684      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
27685      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
27686      */
27687     valueNotFoundText : undefined,
27688     
27689     /**
27690      * @cfg {String} defaultValue The value displayed after loading the store.
27691      */
27692     defaultValue: '',
27693     
27694     /**
27695      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
27696      */
27697     blockFocus : false,
27698     
27699     /**
27700      * @cfg {Boolean} disableClear Disable showing of clear button.
27701      */
27702     disableClear : false,
27703     /**
27704      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
27705      */
27706     alwaysQuery : false,
27707     
27708     //private
27709     addicon : false,
27710     editicon: false,
27711     
27712     // element that contains real text value.. (when hidden is used..)
27713      
27714     // private
27715     onRender : function(ct, position){
27716         Roo.form.Field.prototype.onRender.call(this, ct, position);
27717         
27718         if(this.store){
27719             this.store.on('beforeload', this.onBeforeLoad, this);
27720             this.store.on('load', this.onLoad, this);
27721             this.store.on('loadexception', this.onLoadException, this);
27722             this.store.load({});
27723         }
27724         
27725         
27726         
27727     },
27728
27729     // private
27730     initEvents : function(){
27731         //Roo.form.ComboBox.superclass.initEvents.call(this);
27732  
27733     },
27734
27735     onDestroy : function(){
27736        
27737         if(this.store){
27738             this.store.un('beforeload', this.onBeforeLoad, this);
27739             this.store.un('load', this.onLoad, this);
27740             this.store.un('loadexception', this.onLoadException, this);
27741         }
27742         //Roo.form.ComboBox.superclass.onDestroy.call(this);
27743     },
27744
27745     // private
27746     fireKey : function(e){
27747         if(e.isNavKeyPress() && !this.list.isVisible()){
27748             this.fireEvent("specialkey", this, e);
27749         }
27750     },
27751
27752     // private
27753     onResize: function(w, h){
27754         
27755         return; 
27756     
27757         
27758     },
27759
27760     /**
27761      * Allow or prevent the user from directly editing the field text.  If false is passed,
27762      * the user will only be able to select from the items defined in the dropdown list.  This method
27763      * is the runtime equivalent of setting the 'editable' config option at config time.
27764      * @param {Boolean} value True to allow the user to directly edit the field text
27765      */
27766     setEditable : function(value){
27767          
27768     },
27769
27770     // private
27771     onBeforeLoad : function(){
27772         
27773         Roo.log("Select before load");
27774         return;
27775     
27776         this.innerList.update(this.loadingText ?
27777                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
27778         //this.restrictHeight();
27779         this.selectedIndex = -1;
27780     },
27781
27782     // private
27783     onLoad : function(){
27784
27785     
27786         var dom = this.el.dom;
27787         dom.innerHTML = '';
27788          var od = dom.ownerDocument;
27789          
27790         if (this.emptyText) {
27791             var op = od.createElement('option');
27792             op.setAttribute('value', '');
27793             op.innerHTML = String.format('{0}', this.emptyText);
27794             dom.appendChild(op);
27795         }
27796         if(this.store.getCount() > 0){
27797            
27798             var vf = this.valueField;
27799             var df = this.displayField;
27800             this.store.data.each(function(r) {
27801                 // which colmsn to use... testing - cdoe / title..
27802                 var op = od.createElement('option');
27803                 op.setAttribute('value', r.data[vf]);
27804                 op.innerHTML = String.format('{0}', r.data[df]);
27805                 dom.appendChild(op);
27806             });
27807             if (typeof(this.defaultValue != 'undefined')) {
27808                 this.setValue(this.defaultValue);
27809             }
27810             
27811              
27812         }else{
27813             //this.onEmptyResults();
27814         }
27815         //this.el.focus();
27816     },
27817     // private
27818     onLoadException : function()
27819     {
27820         dom.innerHTML = '';
27821             
27822         Roo.log("Select on load exception");
27823         return;
27824     
27825         this.collapse();
27826         Roo.log(this.store.reader.jsonData);
27827         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
27828             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
27829         }
27830         
27831         
27832     },
27833     // private
27834     onTypeAhead : function(){
27835          
27836     },
27837
27838     // private
27839     onSelect : function(record, index){
27840         Roo.log('on select?');
27841         return;
27842         if(this.fireEvent('beforeselect', this, record, index) !== false){
27843             this.setFromData(index > -1 ? record.data : false);
27844             this.collapse();
27845             this.fireEvent('select', this, record, index);
27846         }
27847     },
27848
27849     /**
27850      * Returns the currently selected field value or empty string if no value is set.
27851      * @return {String} value The selected value
27852      */
27853     getValue : function(){
27854         var dom = this.el.dom;
27855         this.value = dom.options[dom.selectedIndex].value;
27856         return this.value;
27857         
27858     },
27859
27860     /**
27861      * Clears any text/value currently set in the field
27862      */
27863     clearValue : function(){
27864         this.value = '';
27865         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
27866         
27867     },
27868
27869     /**
27870      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
27871      * will be displayed in the field.  If the value does not match the data value of an existing item,
27872      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
27873      * Otherwise the field will be blank (although the value will still be set).
27874      * @param {String} value The value to match
27875      */
27876     setValue : function(v){
27877         var d = this.el.dom;
27878         for (var i =0; i < d.options.length;i++) {
27879             if (v == d.options[i].value) {
27880                 d.selectedIndex = i;
27881                 this.value = v;
27882                 return;
27883             }
27884         }
27885         this.clearValue();
27886     },
27887     /**
27888      * @property {Object} the last set data for the element
27889      */
27890     
27891     lastData : false,
27892     /**
27893      * Sets the value of the field based on a object which is related to the record format for the store.
27894      * @param {Object} value the value to set as. or false on reset?
27895      */
27896     setFromData : function(o){
27897         Roo.log('setfrom data?');
27898          
27899         
27900         
27901     },
27902     // private
27903     reset : function(){
27904         this.clearValue();
27905     },
27906     // private
27907     findRecord : function(prop, value){
27908         
27909         return false;
27910     
27911         var record;
27912         if(this.store.getCount() > 0){
27913             this.store.each(function(r){
27914                 if(r.data[prop] == value){
27915                     record = r;
27916                     return false;
27917                 }
27918                 return true;
27919             });
27920         }
27921         return record;
27922     },
27923     
27924     getName: function()
27925     {
27926         // returns hidden if it's set..
27927         if (!this.rendered) {return ''};
27928         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
27929         
27930     },
27931      
27932
27933     
27934
27935     // private
27936     onEmptyResults : function(){
27937         Roo.log('empty results');
27938         //this.collapse();
27939     },
27940
27941     /**
27942      * Returns true if the dropdown list is expanded, else false.
27943      */
27944     isExpanded : function(){
27945         return false;
27946     },
27947
27948     /**
27949      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
27950      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
27951      * @param {String} value The data value of the item to select
27952      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
27953      * selected item if it is not currently in view (defaults to true)
27954      * @return {Boolean} True if the value matched an item in the list, else false
27955      */
27956     selectByValue : function(v, scrollIntoView){
27957         Roo.log('select By Value');
27958         return false;
27959     
27960         if(v !== undefined && v !== null){
27961             var r = this.findRecord(this.valueField || this.displayField, v);
27962             if(r){
27963                 this.select(this.store.indexOf(r), scrollIntoView);
27964                 return true;
27965             }
27966         }
27967         return false;
27968     },
27969
27970     /**
27971      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
27972      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
27973      * @param {Number} index The zero-based index of the list item to select
27974      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
27975      * selected item if it is not currently in view (defaults to true)
27976      */
27977     select : function(index, scrollIntoView){
27978         Roo.log('select ');
27979         return  ;
27980         
27981         this.selectedIndex = index;
27982         this.view.select(index);
27983         if(scrollIntoView !== false){
27984             var el = this.view.getNode(index);
27985             if(el){
27986                 this.innerList.scrollChildIntoView(el, false);
27987             }
27988         }
27989     },
27990
27991       
27992
27993     // private
27994     validateBlur : function(){
27995         
27996         return;
27997         
27998     },
27999
28000     // private
28001     initQuery : function(){
28002         this.doQuery(this.getRawValue());
28003     },
28004
28005     // private
28006     doForce : function(){
28007         if(this.el.dom.value.length > 0){
28008             this.el.dom.value =
28009                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
28010              
28011         }
28012     },
28013
28014     /**
28015      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
28016      * query allowing the query action to be canceled if needed.
28017      * @param {String} query The SQL query to execute
28018      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
28019      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
28020      * saved in the current store (defaults to false)
28021      */
28022     doQuery : function(q, forceAll){
28023         
28024         Roo.log('doQuery?');
28025         if(q === undefined || q === null){
28026             q = '';
28027         }
28028         var qe = {
28029             query: q,
28030             forceAll: forceAll,
28031             combo: this,
28032             cancel:false
28033         };
28034         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
28035             return false;
28036         }
28037         q = qe.query;
28038         forceAll = qe.forceAll;
28039         if(forceAll === true || (q.length >= this.minChars)){
28040             if(this.lastQuery != q || this.alwaysQuery){
28041                 this.lastQuery = q;
28042                 if(this.mode == 'local'){
28043                     this.selectedIndex = -1;
28044                     if(forceAll){
28045                         this.store.clearFilter();
28046                     }else{
28047                         this.store.filter(this.displayField, q);
28048                     }
28049                     this.onLoad();
28050                 }else{
28051                     this.store.baseParams[this.queryParam] = q;
28052                     this.store.load({
28053                         params: this.getParams(q)
28054                     });
28055                     this.expand();
28056                 }
28057             }else{
28058                 this.selectedIndex = -1;
28059                 this.onLoad();   
28060             }
28061         }
28062     },
28063
28064     // private
28065     getParams : function(q){
28066         var p = {};
28067         //p[this.queryParam] = q;
28068         if(this.pageSize){
28069             p.start = 0;
28070             p.limit = this.pageSize;
28071         }
28072         return p;
28073     },
28074
28075     /**
28076      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
28077      */
28078     collapse : function(){
28079         
28080     },
28081
28082     // private
28083     collapseIf : function(e){
28084         
28085     },
28086
28087     /**
28088      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
28089      */
28090     expand : function(){
28091         
28092     } ,
28093
28094     // private
28095      
28096
28097     /** 
28098     * @cfg {Boolean} grow 
28099     * @hide 
28100     */
28101     /** 
28102     * @cfg {Number} growMin 
28103     * @hide 
28104     */
28105     /** 
28106     * @cfg {Number} growMax 
28107     * @hide 
28108     */
28109     /**
28110      * @hide
28111      * @method autoSize
28112      */
28113     
28114     setWidth : function()
28115     {
28116         
28117     },
28118     getResizeEl : function(){
28119         return this.el;
28120     }
28121 });//<script type="text/javasscript">
28122  
28123
28124 /**
28125  * @class Roo.DDView
28126  * A DnD enabled version of Roo.View.
28127  * @param {Element/String} container The Element in which to create the View.
28128  * @param {String} tpl The template string used to create the markup for each element of the View
28129  * @param {Object} config The configuration properties. These include all the config options of
28130  * {@link Roo.View} plus some specific to this class.<br>
28131  * <p>
28132  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
28133  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
28134  * <p>
28135  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
28136 .x-view-drag-insert-above {
28137         border-top:1px dotted #3366cc;
28138 }
28139 .x-view-drag-insert-below {
28140         border-bottom:1px dotted #3366cc;
28141 }
28142 </code></pre>
28143  * 
28144  */
28145  
28146 Roo.DDView = function(container, tpl, config) {
28147     Roo.DDView.superclass.constructor.apply(this, arguments);
28148     this.getEl().setStyle("outline", "0px none");
28149     this.getEl().unselectable();
28150     if (this.dragGroup) {
28151                 this.setDraggable(this.dragGroup.split(","));
28152     }
28153     if (this.dropGroup) {
28154                 this.setDroppable(this.dropGroup.split(","));
28155     }
28156     if (this.deletable) {
28157         this.setDeletable();
28158     }
28159     this.isDirtyFlag = false;
28160         this.addEvents({
28161                 "drop" : true
28162         });
28163 };
28164
28165 Roo.extend(Roo.DDView, Roo.View, {
28166 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
28167 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
28168 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
28169 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
28170
28171         isFormField: true,
28172
28173         reset: Roo.emptyFn,
28174         
28175         clearInvalid: Roo.form.Field.prototype.clearInvalid,
28176
28177         validate: function() {
28178                 return true;
28179         },
28180         
28181         destroy: function() {
28182                 this.purgeListeners();
28183                 this.getEl.removeAllListeners();
28184                 this.getEl().remove();
28185                 if (this.dragZone) {
28186                         if (this.dragZone.destroy) {
28187                                 this.dragZone.destroy();
28188                         }
28189                 }
28190                 if (this.dropZone) {
28191                         if (this.dropZone.destroy) {
28192                                 this.dropZone.destroy();
28193                         }
28194                 }
28195         },
28196
28197 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
28198         getName: function() {
28199                 return this.name;
28200         },
28201
28202 /**     Loads the View from a JSON string representing the Records to put into the Store. */
28203         setValue: function(v) {
28204                 if (!this.store) {
28205                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
28206                 }
28207                 var data = {};
28208                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
28209                 this.store.proxy = new Roo.data.MemoryProxy(data);
28210                 this.store.load();
28211         },
28212
28213 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
28214         getValue: function() {
28215                 var result = '(';
28216                 this.store.each(function(rec) {
28217                         result += rec.id + ',';
28218                 });
28219                 return result.substr(0, result.length - 1) + ')';
28220         },
28221         
28222         getIds: function() {
28223                 var i = 0, result = new Array(this.store.getCount());
28224                 this.store.each(function(rec) {
28225                         result[i++] = rec.id;
28226                 });
28227                 return result;
28228         },
28229         
28230         isDirty: function() {
28231                 return this.isDirtyFlag;
28232         },
28233
28234 /**
28235  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
28236  *      whole Element becomes the target, and this causes the drop gesture to append.
28237  */
28238     getTargetFromEvent : function(e) {
28239                 var target = e.getTarget();
28240                 while ((target !== null) && (target.parentNode != this.el.dom)) {
28241                 target = target.parentNode;
28242                 }
28243                 if (!target) {
28244                         target = this.el.dom.lastChild || this.el.dom;
28245                 }
28246                 return target;
28247     },
28248
28249 /**
28250  *      Create the drag data which consists of an object which has the property "ddel" as
28251  *      the drag proxy element. 
28252  */
28253     getDragData : function(e) {
28254         var target = this.findItemFromChild(e.getTarget());
28255                 if(target) {
28256                         this.handleSelection(e);
28257                         var selNodes = this.getSelectedNodes();
28258             var dragData = {
28259                 source: this,
28260                 copy: this.copy || (this.allowCopy && e.ctrlKey),
28261                 nodes: selNodes,
28262                 records: []
28263                         };
28264                         var selectedIndices = this.getSelectedIndexes();
28265                         for (var i = 0; i < selectedIndices.length; i++) {
28266                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
28267                         }
28268                         if (selNodes.length == 1) {
28269                                 dragData.ddel = target.cloneNode(true); // the div element
28270                         } else {
28271                                 var div = document.createElement('div'); // create the multi element drag "ghost"
28272                                 div.className = 'multi-proxy';
28273                                 for (var i = 0, len = selNodes.length; i < len; i++) {
28274                                         div.appendChild(selNodes[i].cloneNode(true));
28275                                 }
28276                                 dragData.ddel = div;
28277                         }
28278             //console.log(dragData)
28279             //console.log(dragData.ddel.innerHTML)
28280                         return dragData;
28281                 }
28282         //console.log('nodragData')
28283                 return false;
28284     },
28285     
28286 /**     Specify to which ddGroup items in this DDView may be dragged. */
28287     setDraggable: function(ddGroup) {
28288         if (ddGroup instanceof Array) {
28289                 Roo.each(ddGroup, this.setDraggable, this);
28290                 return;
28291         }
28292         if (this.dragZone) {
28293                 this.dragZone.addToGroup(ddGroup);
28294         } else {
28295                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
28296                                 containerScroll: true,
28297                                 ddGroup: ddGroup 
28298
28299                         });
28300 //                      Draggability implies selection. DragZone's mousedown selects the element.
28301                         if (!this.multiSelect) { this.singleSelect = true; }
28302
28303 //                      Wire the DragZone's handlers up to methods in *this*
28304                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
28305                 }
28306     },
28307
28308 /**     Specify from which ddGroup this DDView accepts drops. */
28309     setDroppable: function(ddGroup) {
28310         if (ddGroup instanceof Array) {
28311                 Roo.each(ddGroup, this.setDroppable, this);
28312                 return;
28313         }
28314         if (this.dropZone) {
28315                 this.dropZone.addToGroup(ddGroup);
28316         } else {
28317                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
28318                                 containerScroll: true,
28319                                 ddGroup: ddGroup
28320                         });
28321
28322 //                      Wire the DropZone's handlers up to methods in *this*
28323                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
28324                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
28325                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
28326                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
28327                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
28328                 }
28329     },
28330
28331 /**     Decide whether to drop above or below a View node. */
28332     getDropPoint : function(e, n, dd){
28333         if (n == this.el.dom) { return "above"; }
28334                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
28335                 var c = t + (b - t) / 2;
28336                 var y = Roo.lib.Event.getPageY(e);
28337                 if(y <= c) {
28338                         return "above";
28339                 }else{
28340                         return "below";
28341                 }
28342     },
28343
28344     onNodeEnter : function(n, dd, e, data){
28345                 return false;
28346     },
28347     
28348     onNodeOver : function(n, dd, e, data){
28349                 var pt = this.getDropPoint(e, n, dd);
28350                 // set the insert point style on the target node
28351                 var dragElClass = this.dropNotAllowed;
28352                 if (pt) {
28353                         var targetElClass;
28354                         if (pt == "above"){
28355                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
28356                                 targetElClass = "x-view-drag-insert-above";
28357                         } else {
28358                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
28359                                 targetElClass = "x-view-drag-insert-below";
28360                         }
28361                         if (this.lastInsertClass != targetElClass){
28362                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
28363                                 this.lastInsertClass = targetElClass;
28364                         }
28365                 }
28366                 return dragElClass;
28367         },
28368
28369     onNodeOut : function(n, dd, e, data){
28370                 this.removeDropIndicators(n);
28371     },
28372
28373     onNodeDrop : function(n, dd, e, data){
28374         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
28375                 return false;
28376         }
28377         var pt = this.getDropPoint(e, n, dd);
28378                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
28379                 if (pt == "below") { insertAt++; }
28380                 for (var i = 0; i < data.records.length; i++) {
28381                         var r = data.records[i];
28382                         var dup = this.store.getById(r.id);
28383                         if (dup && (dd != this.dragZone)) {
28384                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
28385                         } else {
28386                                 if (data.copy) {
28387                                         this.store.insert(insertAt++, r.copy());
28388                                 } else {
28389                                         data.source.isDirtyFlag = true;
28390                                         r.store.remove(r);
28391                                         this.store.insert(insertAt++, r);
28392                                 }
28393                                 this.isDirtyFlag = true;
28394                         }
28395                 }
28396                 this.dragZone.cachedTarget = null;
28397                 return true;
28398     },
28399
28400     removeDropIndicators : function(n){
28401                 if(n){
28402                         Roo.fly(n).removeClass([
28403                                 "x-view-drag-insert-above",
28404                                 "x-view-drag-insert-below"]);
28405                         this.lastInsertClass = "_noclass";
28406                 }
28407     },
28408
28409 /**
28410  *      Utility method. Add a delete option to the DDView's context menu.
28411  *      @param {String} imageUrl The URL of the "delete" icon image.
28412  */
28413         setDeletable: function(imageUrl) {
28414                 if (!this.singleSelect && !this.multiSelect) {
28415                         this.singleSelect = true;
28416                 }
28417                 var c = this.getContextMenu();
28418                 this.contextMenu.on("itemclick", function(item) {
28419                         switch (item.id) {
28420                                 case "delete":
28421                                         this.remove(this.getSelectedIndexes());
28422                                         break;
28423                         }
28424                 }, this);
28425                 this.contextMenu.add({
28426                         icon: imageUrl,
28427                         id: "delete",
28428                         text: 'Delete'
28429                 });
28430         },
28431         
28432 /**     Return the context menu for this DDView. */
28433         getContextMenu: function() {
28434                 if (!this.contextMenu) {
28435 //                      Create the View's context menu
28436                         this.contextMenu = new Roo.menu.Menu({
28437                                 id: this.id + "-contextmenu"
28438                         });
28439                         this.el.on("contextmenu", this.showContextMenu, this);
28440                 }
28441                 return this.contextMenu;
28442         },
28443         
28444         disableContextMenu: function() {
28445                 if (this.contextMenu) {
28446                         this.el.un("contextmenu", this.showContextMenu, this);
28447                 }
28448         },
28449
28450         showContextMenu: function(e, item) {
28451         item = this.findItemFromChild(e.getTarget());
28452                 if (item) {
28453                         e.stopEvent();
28454                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
28455                         this.contextMenu.showAt(e.getXY());
28456             }
28457     },
28458
28459 /**
28460  *      Remove {@link Roo.data.Record}s at the specified indices.
28461  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
28462  */
28463     remove: function(selectedIndices) {
28464                 selectedIndices = [].concat(selectedIndices);
28465                 for (var i = 0; i < selectedIndices.length; i++) {
28466                         var rec = this.store.getAt(selectedIndices[i]);
28467                         this.store.remove(rec);
28468                 }
28469     },
28470
28471 /**
28472  *      Double click fires the event, but also, if this is draggable, and there is only one other
28473  *      related DropZone, it transfers the selected node.
28474  */
28475     onDblClick : function(e){
28476         var item = this.findItemFromChild(e.getTarget());
28477         if(item){
28478             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
28479                 return false;
28480             }
28481             if (this.dragGroup) {
28482                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
28483                     while (targets.indexOf(this.dropZone) > -1) {
28484                             targets.remove(this.dropZone);
28485                                 }
28486                     if (targets.length == 1) {
28487                                         this.dragZone.cachedTarget = null;
28488                         var el = Roo.get(targets[0].getEl());
28489                         var box = el.getBox(true);
28490                         targets[0].onNodeDrop(el.dom, {
28491                                 target: el.dom,
28492                                 xy: [box.x, box.y + box.height - 1]
28493                         }, null, this.getDragData(e));
28494                     }
28495                 }
28496         }
28497     },
28498     
28499     handleSelection: function(e) {
28500                 this.dragZone.cachedTarget = null;
28501         var item = this.findItemFromChild(e.getTarget());
28502         if (!item) {
28503                 this.clearSelections(true);
28504                 return;
28505         }
28506                 if (item && (this.multiSelect || this.singleSelect)){
28507                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
28508                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
28509                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
28510                                 this.unselect(item);
28511                         } else {
28512                                 this.select(item, this.multiSelect && e.ctrlKey);
28513                                 this.lastSelection = item;
28514                         }
28515                 }
28516     },
28517
28518     onItemClick : function(item, index, e){
28519                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
28520                         return false;
28521                 }
28522                 return true;
28523     },
28524
28525     unselect : function(nodeInfo, suppressEvent){
28526                 var node = this.getNode(nodeInfo);
28527                 if(node && this.isSelected(node)){
28528                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28529                                 Roo.fly(node).removeClass(this.selectedClass);
28530                                 this.selections.remove(node);
28531                                 if(!suppressEvent){
28532                                         this.fireEvent("selectionchange", this, this.selections);
28533                                 }
28534                         }
28535                 }
28536     }
28537 });
28538 /*
28539  * Based on:
28540  * Ext JS Library 1.1.1
28541  * Copyright(c) 2006-2007, Ext JS, LLC.
28542  *
28543  * Originally Released Under LGPL - original licence link has changed is not relivant.
28544  *
28545  * Fork - LGPL
28546  * <script type="text/javascript">
28547  */
28548  
28549 /**
28550  * @class Roo.LayoutManager
28551  * @extends Roo.util.Observable
28552  * Base class for layout managers.
28553  */
28554 Roo.LayoutManager = function(container, config){
28555     Roo.LayoutManager.superclass.constructor.call(this);
28556     this.el = Roo.get(container);
28557     // ie scrollbar fix
28558     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
28559         document.body.scroll = "no";
28560     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
28561         this.el.position('relative');
28562     }
28563     this.id = this.el.id;
28564     this.el.addClass("x-layout-container");
28565     /** false to disable window resize monitoring @type Boolean */
28566     this.monitorWindowResize = true;
28567     this.regions = {};
28568     this.addEvents({
28569         /**
28570          * @event layout
28571          * Fires when a layout is performed. 
28572          * @param {Roo.LayoutManager} this
28573          */
28574         "layout" : true,
28575         /**
28576          * @event regionresized
28577          * Fires when the user resizes a region. 
28578          * @param {Roo.LayoutRegion} region The resized region
28579          * @param {Number} newSize The new size (width for east/west, height for north/south)
28580          */
28581         "regionresized" : true,
28582         /**
28583          * @event regioncollapsed
28584          * Fires when a region is collapsed. 
28585          * @param {Roo.LayoutRegion} region The collapsed region
28586          */
28587         "regioncollapsed" : true,
28588         /**
28589          * @event regionexpanded
28590          * Fires when a region is expanded.  
28591          * @param {Roo.LayoutRegion} region The expanded region
28592          */
28593         "regionexpanded" : true
28594     });
28595     this.updating = false;
28596     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
28597 };
28598
28599 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
28600     /**
28601      * Returns true if this layout is currently being updated
28602      * @return {Boolean}
28603      */
28604     isUpdating : function(){
28605         return this.updating; 
28606     },
28607     
28608     /**
28609      * Suspend the LayoutManager from doing auto-layouts while
28610      * making multiple add or remove calls
28611      */
28612     beginUpdate : function(){
28613         this.updating = true;    
28614     },
28615     
28616     /**
28617      * Restore auto-layouts and optionally disable the manager from performing a layout
28618      * @param {Boolean} noLayout true to disable a layout update 
28619      */
28620     endUpdate : function(noLayout){
28621         this.updating = false;
28622         if(!noLayout){
28623             this.layout();
28624         }    
28625     },
28626     
28627     layout: function(){
28628         
28629     },
28630     
28631     onRegionResized : function(region, newSize){
28632         this.fireEvent("regionresized", region, newSize);
28633         this.layout();
28634     },
28635     
28636     onRegionCollapsed : function(region){
28637         this.fireEvent("regioncollapsed", region);
28638     },
28639     
28640     onRegionExpanded : function(region){
28641         this.fireEvent("regionexpanded", region);
28642     },
28643         
28644     /**
28645      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
28646      * performs box-model adjustments.
28647      * @return {Object} The size as an object {width: (the width), height: (the height)}
28648      */
28649     getViewSize : function(){
28650         var size;
28651         if(this.el.dom != document.body){
28652             size = this.el.getSize();
28653         }else{
28654             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
28655         }
28656         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
28657         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
28658         return size;
28659     },
28660     
28661     /**
28662      * Returns the Element this layout is bound to.
28663      * @return {Roo.Element}
28664      */
28665     getEl : function(){
28666         return this.el;
28667     },
28668     
28669     /**
28670      * Returns the specified region.
28671      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
28672      * @return {Roo.LayoutRegion}
28673      */
28674     getRegion : function(target){
28675         return this.regions[target.toLowerCase()];
28676     },
28677     
28678     onWindowResize : function(){
28679         if(this.monitorWindowResize){
28680             this.layout();
28681         }
28682     }
28683 });/*
28684  * Based on:
28685  * Ext JS Library 1.1.1
28686  * Copyright(c) 2006-2007, Ext JS, LLC.
28687  *
28688  * Originally Released Under LGPL - original licence link has changed is not relivant.
28689  *
28690  * Fork - LGPL
28691  * <script type="text/javascript">
28692  */
28693 /**
28694  * @class Roo.BorderLayout
28695  * @extends Roo.LayoutManager
28696  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
28697  * please see: <br><br>
28698  * <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>
28699  * <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>
28700  * Example:
28701  <pre><code>
28702  var layout = new Roo.BorderLayout(document.body, {
28703     north: {
28704         initialSize: 25,
28705         titlebar: false
28706     },
28707     west: {
28708         split:true,
28709         initialSize: 200,
28710         minSize: 175,
28711         maxSize: 400,
28712         titlebar: true,
28713         collapsible: true
28714     },
28715     east: {
28716         split:true,
28717         initialSize: 202,
28718         minSize: 175,
28719         maxSize: 400,
28720         titlebar: true,
28721         collapsible: true
28722     },
28723     south: {
28724         split:true,
28725         initialSize: 100,
28726         minSize: 100,
28727         maxSize: 200,
28728         titlebar: true,
28729         collapsible: true
28730     },
28731     center: {
28732         titlebar: true,
28733         autoScroll:true,
28734         resizeTabs: true,
28735         minTabWidth: 50,
28736         preferredTabWidth: 150
28737     }
28738 });
28739
28740 // shorthand
28741 var CP = Roo.ContentPanel;
28742
28743 layout.beginUpdate();
28744 layout.add("north", new CP("north", "North"));
28745 layout.add("south", new CP("south", {title: "South", closable: true}));
28746 layout.add("west", new CP("west", {title: "West"}));
28747 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
28748 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
28749 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
28750 layout.getRegion("center").showPanel("center1");
28751 layout.endUpdate();
28752 </code></pre>
28753
28754 <b>The container the layout is rendered into can be either the body element or any other element.
28755 If it is not the body element, the container needs to either be an absolute positioned element,
28756 or you will need to add "position:relative" to the css of the container.  You will also need to specify
28757 the container size if it is not the body element.</b>
28758
28759 * @constructor
28760 * Create a new BorderLayout
28761 * @param {String/HTMLElement/Element} container The container this layout is bound to
28762 * @param {Object} config Configuration options
28763  */
28764 Roo.BorderLayout = function(container, config){
28765     config = config || {};
28766     Roo.BorderLayout.superclass.constructor.call(this, container, config);
28767     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
28768     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
28769         var target = this.factory.validRegions[i];
28770         if(config[target]){
28771             this.addRegion(target, config[target]);
28772         }
28773     }
28774 };
28775
28776 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
28777     /**
28778      * Creates and adds a new region if it doesn't already exist.
28779      * @param {String} target The target region key (north, south, east, west or center).
28780      * @param {Object} config The regions config object
28781      * @return {BorderLayoutRegion} The new region
28782      */
28783     addRegion : function(target, config){
28784         if(!this.regions[target]){
28785             var r = this.factory.create(target, this, config);
28786             this.bindRegion(target, r);
28787         }
28788         return this.regions[target];
28789     },
28790
28791     // private (kinda)
28792     bindRegion : function(name, r){
28793         this.regions[name] = r;
28794         r.on("visibilitychange", this.layout, this);
28795         r.on("paneladded", this.layout, this);
28796         r.on("panelremoved", this.layout, this);
28797         r.on("invalidated", this.layout, this);
28798         r.on("resized", this.onRegionResized, this);
28799         r.on("collapsed", this.onRegionCollapsed, this);
28800         r.on("expanded", this.onRegionExpanded, this);
28801     },
28802
28803     /**
28804      * Performs a layout update.
28805      */
28806     layout : function(){
28807         if(this.updating) {
28808             return;
28809         }
28810         var size = this.getViewSize();
28811         var w = size.width;
28812         var h = size.height;
28813         var centerW = w;
28814         var centerH = h;
28815         var centerY = 0;
28816         var centerX = 0;
28817         //var x = 0, y = 0;
28818
28819         var rs = this.regions;
28820         var north = rs["north"];
28821         var south = rs["south"]; 
28822         var west = rs["west"];
28823         var east = rs["east"];
28824         var center = rs["center"];
28825         //if(this.hideOnLayout){ // not supported anymore
28826             //c.el.setStyle("display", "none");
28827         //}
28828         if(north && north.isVisible()){
28829             var b = north.getBox();
28830             var m = north.getMargins();
28831             b.width = w - (m.left+m.right);
28832             b.x = m.left;
28833             b.y = m.top;
28834             centerY = b.height + b.y + m.bottom;
28835             centerH -= centerY;
28836             north.updateBox(this.safeBox(b));
28837         }
28838         if(south && south.isVisible()){
28839             var b = south.getBox();
28840             var m = south.getMargins();
28841             b.width = w - (m.left+m.right);
28842             b.x = m.left;
28843             var totalHeight = (b.height + m.top + m.bottom);
28844             b.y = h - totalHeight + m.top;
28845             centerH -= totalHeight;
28846             south.updateBox(this.safeBox(b));
28847         }
28848         if(west && west.isVisible()){
28849             var b = west.getBox();
28850             var m = west.getMargins();
28851             b.height = centerH - (m.top+m.bottom);
28852             b.x = m.left;
28853             b.y = centerY + m.top;
28854             var totalWidth = (b.width + m.left + m.right);
28855             centerX += totalWidth;
28856             centerW -= totalWidth;
28857             west.updateBox(this.safeBox(b));
28858         }
28859         if(east && east.isVisible()){
28860             var b = east.getBox();
28861             var m = east.getMargins();
28862             b.height = centerH - (m.top+m.bottom);
28863             var totalWidth = (b.width + m.left + m.right);
28864             b.x = w - totalWidth + m.left;
28865             b.y = centerY + m.top;
28866             centerW -= totalWidth;
28867             east.updateBox(this.safeBox(b));
28868         }
28869         if(center){
28870             var m = center.getMargins();
28871             var centerBox = {
28872                 x: centerX + m.left,
28873                 y: centerY + m.top,
28874                 width: centerW - (m.left+m.right),
28875                 height: centerH - (m.top+m.bottom)
28876             };
28877             //if(this.hideOnLayout){
28878                 //center.el.setStyle("display", "block");
28879             //}
28880             center.updateBox(this.safeBox(centerBox));
28881         }
28882         this.el.repaint();
28883         this.fireEvent("layout", this);
28884     },
28885
28886     // private
28887     safeBox : function(box){
28888         box.width = Math.max(0, box.width);
28889         box.height = Math.max(0, box.height);
28890         return box;
28891     },
28892
28893     /**
28894      * Adds a ContentPanel (or subclass) to this layout.
28895      * @param {String} target The target region key (north, south, east, west or center).
28896      * @param {Roo.ContentPanel} panel The panel to add
28897      * @return {Roo.ContentPanel} The added panel
28898      */
28899     add : function(target, panel){
28900          
28901         target = target.toLowerCase();
28902         return this.regions[target].add(panel);
28903     },
28904
28905     /**
28906      * Remove a ContentPanel (or subclass) to this layout.
28907      * @param {String} target The target region key (north, south, east, west or center).
28908      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
28909      * @return {Roo.ContentPanel} The removed panel
28910      */
28911     remove : function(target, panel){
28912         target = target.toLowerCase();
28913         return this.regions[target].remove(panel);
28914     },
28915
28916     /**
28917      * Searches all regions for a panel with the specified id
28918      * @param {String} panelId
28919      * @return {Roo.ContentPanel} The panel or null if it wasn't found
28920      */
28921     findPanel : function(panelId){
28922         var rs = this.regions;
28923         for(var target in rs){
28924             if(typeof rs[target] != "function"){
28925                 var p = rs[target].getPanel(panelId);
28926                 if(p){
28927                     return p;
28928                 }
28929             }
28930         }
28931         return null;
28932     },
28933
28934     /**
28935      * Searches all regions for a panel with the specified id and activates (shows) it.
28936      * @param {String/ContentPanel} panelId The panels id or the panel itself
28937      * @return {Roo.ContentPanel} The shown panel or null
28938      */
28939     showPanel : function(panelId) {
28940       var rs = this.regions;
28941       for(var target in rs){
28942          var r = rs[target];
28943          if(typeof r != "function"){
28944             if(r.hasPanel(panelId)){
28945                return r.showPanel(panelId);
28946             }
28947          }
28948       }
28949       return null;
28950    },
28951
28952    /**
28953      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
28954      * @param {Roo.state.Provider} provider (optional) An alternate state provider
28955      */
28956     restoreState : function(provider){
28957         if(!provider){
28958             provider = Roo.state.Manager;
28959         }
28960         var sm = new Roo.LayoutStateManager();
28961         sm.init(this, provider);
28962     },
28963
28964     /**
28965      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
28966      * object should contain properties for each region to add ContentPanels to, and each property's value should be
28967      * a valid ContentPanel config object.  Example:
28968      * <pre><code>
28969 // Create the main layout
28970 var layout = new Roo.BorderLayout('main-ct', {
28971     west: {
28972         split:true,
28973         minSize: 175,
28974         titlebar: true
28975     },
28976     center: {
28977         title:'Components'
28978     }
28979 }, 'main-ct');
28980
28981 // Create and add multiple ContentPanels at once via configs
28982 layout.batchAdd({
28983    west: {
28984        id: 'source-files',
28985        autoCreate:true,
28986        title:'Ext Source Files',
28987        autoScroll:true,
28988        fitToFrame:true
28989    },
28990    center : {
28991        el: cview,
28992        autoScroll:true,
28993        fitToFrame:true,
28994        toolbar: tb,
28995        resizeEl:'cbody'
28996    }
28997 });
28998 </code></pre>
28999      * @param {Object} regions An object containing ContentPanel configs by region name
29000      */
29001     batchAdd : function(regions){
29002         this.beginUpdate();
29003         for(var rname in regions){
29004             var lr = this.regions[rname];
29005             if(lr){
29006                 this.addTypedPanels(lr, regions[rname]);
29007             }
29008         }
29009         this.endUpdate();
29010     },
29011
29012     // private
29013     addTypedPanels : function(lr, ps){
29014         if(typeof ps == 'string'){
29015             lr.add(new Roo.ContentPanel(ps));
29016         }
29017         else if(ps instanceof Array){
29018             for(var i =0, len = ps.length; i < len; i++){
29019                 this.addTypedPanels(lr, ps[i]);
29020             }
29021         }
29022         else if(!ps.events){ // raw config?
29023             var el = ps.el;
29024             delete ps.el; // prevent conflict
29025             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
29026         }
29027         else {  // panel object assumed!
29028             lr.add(ps);
29029         }
29030     },
29031     /**
29032      * Adds a xtype elements to the layout.
29033      * <pre><code>
29034
29035 layout.addxtype({
29036        xtype : 'ContentPanel',
29037        region: 'west',
29038        items: [ .... ]
29039    }
29040 );
29041
29042 layout.addxtype({
29043         xtype : 'NestedLayoutPanel',
29044         region: 'west',
29045         layout: {
29046            center: { },
29047            west: { }   
29048         },
29049         items : [ ... list of content panels or nested layout panels.. ]
29050    }
29051 );
29052 </code></pre>
29053      * @param {Object} cfg Xtype definition of item to add.
29054      */
29055     addxtype : function(cfg)
29056     {
29057         // basically accepts a pannel...
29058         // can accept a layout region..!?!?
29059         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
29060         
29061         if (!cfg.xtype.match(/Panel$/)) {
29062             return false;
29063         }
29064         var ret = false;
29065         
29066         if (typeof(cfg.region) == 'undefined') {
29067             Roo.log("Failed to add Panel, region was not set");
29068             Roo.log(cfg);
29069             return false;
29070         }
29071         var region = cfg.region;
29072         delete cfg.region;
29073         
29074           
29075         var xitems = [];
29076         if (cfg.items) {
29077             xitems = cfg.items;
29078             delete cfg.items;
29079         }
29080         var nb = false;
29081         
29082         switch(cfg.xtype) 
29083         {
29084             case 'ContentPanel':  // ContentPanel (el, cfg)
29085             case 'ScrollPanel':  // ContentPanel (el, cfg)
29086             case 'ViewPanel': 
29087                 if(cfg.autoCreate) {
29088                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29089                 } else {
29090                     var el = this.el.createChild();
29091                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
29092                 }
29093                 
29094                 this.add(region, ret);
29095                 break;
29096             
29097             
29098             case 'TreePanel': // our new panel!
29099                 cfg.el = this.el.createChild();
29100                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29101                 this.add(region, ret);
29102                 break;
29103             
29104             case 'NestedLayoutPanel': 
29105                 // create a new Layout (which is  a Border Layout...
29106                 var el = this.el.createChild();
29107                 var clayout = cfg.layout;
29108                 delete cfg.layout;
29109                 clayout.items   = clayout.items  || [];
29110                 // replace this exitems with the clayout ones..
29111                 xitems = clayout.items;
29112                  
29113                 
29114                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
29115                     cfg.background = false;
29116                 }
29117                 var layout = new Roo.BorderLayout(el, clayout);
29118                 
29119                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
29120                 //console.log('adding nested layout panel '  + cfg.toSource());
29121                 this.add(region, ret);
29122                 nb = {}; /// find first...
29123                 break;
29124                 
29125             case 'GridPanel': 
29126             
29127                 // needs grid and region
29128                 
29129                 //var el = this.getRegion(region).el.createChild();
29130                 var el = this.el.createChild();
29131                 // create the grid first...
29132                 
29133                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
29134                 delete cfg.grid;
29135                 if (region == 'center' && this.active ) {
29136                     cfg.background = false;
29137                 }
29138                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
29139                 
29140                 this.add(region, ret);
29141                 if (cfg.background) {
29142                     ret.on('activate', function(gp) {
29143                         if (!gp.grid.rendered) {
29144                             gp.grid.render();
29145                         }
29146                     });
29147                 } else {
29148                     grid.render();
29149                 }
29150                 break;
29151            
29152            
29153            
29154                 
29155                 
29156                 
29157             default:
29158                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
29159                     
29160                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29161                     this.add(region, ret);
29162                 } else {
29163                 
29164                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
29165                     return null;
29166                 }
29167                 
29168              // GridPanel (grid, cfg)
29169             
29170         }
29171         this.beginUpdate();
29172         // add children..
29173         var region = '';
29174         var abn = {};
29175         Roo.each(xitems, function(i)  {
29176             region = nb && i.region ? i.region : false;
29177             
29178             var add = ret.addxtype(i);
29179            
29180             if (region) {
29181                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
29182                 if (!i.background) {
29183                     abn[region] = nb[region] ;
29184                 }
29185             }
29186             
29187         });
29188         this.endUpdate();
29189
29190         // make the last non-background panel active..
29191         //if (nb) { Roo.log(abn); }
29192         if (nb) {
29193             
29194             for(var r in abn) {
29195                 region = this.getRegion(r);
29196                 if (region) {
29197                     // tried using nb[r], but it does not work..
29198                      
29199                     region.showPanel(abn[r]);
29200                    
29201                 }
29202             }
29203         }
29204         return ret;
29205         
29206     }
29207 });
29208
29209 /**
29210  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
29211  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
29212  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
29213  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
29214  * <pre><code>
29215 // shorthand
29216 var CP = Roo.ContentPanel;
29217
29218 var layout = Roo.BorderLayout.create({
29219     north: {
29220         initialSize: 25,
29221         titlebar: false,
29222         panels: [new CP("north", "North")]
29223     },
29224     west: {
29225         split:true,
29226         initialSize: 200,
29227         minSize: 175,
29228         maxSize: 400,
29229         titlebar: true,
29230         collapsible: true,
29231         panels: [new CP("west", {title: "West"})]
29232     },
29233     east: {
29234         split:true,
29235         initialSize: 202,
29236         minSize: 175,
29237         maxSize: 400,
29238         titlebar: true,
29239         collapsible: true,
29240         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
29241     },
29242     south: {
29243         split:true,
29244         initialSize: 100,
29245         minSize: 100,
29246         maxSize: 200,
29247         titlebar: true,
29248         collapsible: true,
29249         panels: [new CP("south", {title: "South", closable: true})]
29250     },
29251     center: {
29252         titlebar: true,
29253         autoScroll:true,
29254         resizeTabs: true,
29255         minTabWidth: 50,
29256         preferredTabWidth: 150,
29257         panels: [
29258             new CP("center1", {title: "Close Me", closable: true}),
29259             new CP("center2", {title: "Center Panel", closable: false})
29260         ]
29261     }
29262 }, document.body);
29263
29264 layout.getRegion("center").showPanel("center1");
29265 </code></pre>
29266  * @param config
29267  * @param targetEl
29268  */
29269 Roo.BorderLayout.create = function(config, targetEl){
29270     var layout = new Roo.BorderLayout(targetEl || document.body, config);
29271     layout.beginUpdate();
29272     var regions = Roo.BorderLayout.RegionFactory.validRegions;
29273     for(var j = 0, jlen = regions.length; j < jlen; j++){
29274         var lr = regions[j];
29275         if(layout.regions[lr] && config[lr].panels){
29276             var r = layout.regions[lr];
29277             var ps = config[lr].panels;
29278             layout.addTypedPanels(r, ps);
29279         }
29280     }
29281     layout.endUpdate();
29282     return layout;
29283 };
29284
29285 // private
29286 Roo.BorderLayout.RegionFactory = {
29287     // private
29288     validRegions : ["north","south","east","west","center"],
29289
29290     // private
29291     create : function(target, mgr, config){
29292         target = target.toLowerCase();
29293         if(config.lightweight || config.basic){
29294             return new Roo.BasicLayoutRegion(mgr, config, target);
29295         }
29296         switch(target){
29297             case "north":
29298                 return new Roo.NorthLayoutRegion(mgr, config);
29299             case "south":
29300                 return new Roo.SouthLayoutRegion(mgr, config);
29301             case "east":
29302                 return new Roo.EastLayoutRegion(mgr, config);
29303             case "west":
29304                 return new Roo.WestLayoutRegion(mgr, config);
29305             case "center":
29306                 return new Roo.CenterLayoutRegion(mgr, config);
29307         }
29308         throw 'Layout region "'+target+'" not supported.';
29309     }
29310 };/*
29311  * Based on:
29312  * Ext JS Library 1.1.1
29313  * Copyright(c) 2006-2007, Ext JS, LLC.
29314  *
29315  * Originally Released Under LGPL - original licence link has changed is not relivant.
29316  *
29317  * Fork - LGPL
29318  * <script type="text/javascript">
29319  */
29320  
29321 /**
29322  * @class Roo.BasicLayoutRegion
29323  * @extends Roo.util.Observable
29324  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
29325  * and does not have a titlebar, tabs or any other features. All it does is size and position 
29326  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
29327  */
29328 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
29329     this.mgr = mgr;
29330     this.position  = pos;
29331     this.events = {
29332         /**
29333          * @scope Roo.BasicLayoutRegion
29334          */
29335         
29336         /**
29337          * @event beforeremove
29338          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
29339          * @param {Roo.LayoutRegion} this
29340          * @param {Roo.ContentPanel} panel The panel
29341          * @param {Object} e The cancel event object
29342          */
29343         "beforeremove" : true,
29344         /**
29345          * @event invalidated
29346          * Fires when the layout for this region is changed.
29347          * @param {Roo.LayoutRegion} this
29348          */
29349         "invalidated" : true,
29350         /**
29351          * @event visibilitychange
29352          * Fires when this region is shown or hidden 
29353          * @param {Roo.LayoutRegion} this
29354          * @param {Boolean} visibility true or false
29355          */
29356         "visibilitychange" : true,
29357         /**
29358          * @event paneladded
29359          * Fires when a panel is added. 
29360          * @param {Roo.LayoutRegion} this
29361          * @param {Roo.ContentPanel} panel The panel
29362          */
29363         "paneladded" : true,
29364         /**
29365          * @event panelremoved
29366          * Fires when a panel is removed. 
29367          * @param {Roo.LayoutRegion} this
29368          * @param {Roo.ContentPanel} panel The panel
29369          */
29370         "panelremoved" : true,
29371         /**
29372          * @event beforecollapse
29373          * Fires when this region before collapse.
29374          * @param {Roo.LayoutRegion} this
29375          */
29376         "beforecollapse" : true,
29377         /**
29378          * @event collapsed
29379          * Fires when this region is collapsed.
29380          * @param {Roo.LayoutRegion} this
29381          */
29382         "collapsed" : true,
29383         /**
29384          * @event expanded
29385          * Fires when this region is expanded.
29386          * @param {Roo.LayoutRegion} this
29387          */
29388         "expanded" : true,
29389         /**
29390          * @event slideshow
29391          * Fires when this region is slid into view.
29392          * @param {Roo.LayoutRegion} this
29393          */
29394         "slideshow" : true,
29395         /**
29396          * @event slidehide
29397          * Fires when this region slides out of view. 
29398          * @param {Roo.LayoutRegion} this
29399          */
29400         "slidehide" : true,
29401         /**
29402          * @event panelactivated
29403          * Fires when a panel is activated. 
29404          * @param {Roo.LayoutRegion} this
29405          * @param {Roo.ContentPanel} panel The activated panel
29406          */
29407         "panelactivated" : true,
29408         /**
29409          * @event resized
29410          * Fires when the user resizes this region. 
29411          * @param {Roo.LayoutRegion} this
29412          * @param {Number} newSize The new size (width for east/west, height for north/south)
29413          */
29414         "resized" : true
29415     };
29416     /** A collection of panels in this region. @type Roo.util.MixedCollection */
29417     this.panels = new Roo.util.MixedCollection();
29418     this.panels.getKey = this.getPanelId.createDelegate(this);
29419     this.box = null;
29420     this.activePanel = null;
29421     // ensure listeners are added...
29422     
29423     if (config.listeners || config.events) {
29424         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
29425             listeners : config.listeners || {},
29426             events : config.events || {}
29427         });
29428     }
29429     
29430     if(skipConfig !== true){
29431         this.applyConfig(config);
29432     }
29433 };
29434
29435 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
29436     getPanelId : function(p){
29437         return p.getId();
29438     },
29439     
29440     applyConfig : function(config){
29441         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29442         this.config = config;
29443         
29444     },
29445     
29446     /**
29447      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
29448      * the width, for horizontal (north, south) the height.
29449      * @param {Number} newSize The new width or height
29450      */
29451     resizeTo : function(newSize){
29452         var el = this.el ? this.el :
29453                  (this.activePanel ? this.activePanel.getEl() : null);
29454         if(el){
29455             switch(this.position){
29456                 case "east":
29457                 case "west":
29458                     el.setWidth(newSize);
29459                     this.fireEvent("resized", this, newSize);
29460                 break;
29461                 case "north":
29462                 case "south":
29463                     el.setHeight(newSize);
29464                     this.fireEvent("resized", this, newSize);
29465                 break;                
29466             }
29467         }
29468     },
29469     
29470     getBox : function(){
29471         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
29472     },
29473     
29474     getMargins : function(){
29475         return this.margins;
29476     },
29477     
29478     updateBox : function(box){
29479         this.box = box;
29480         var el = this.activePanel.getEl();
29481         el.dom.style.left = box.x + "px";
29482         el.dom.style.top = box.y + "px";
29483         this.activePanel.setSize(box.width, box.height);
29484     },
29485     
29486     /**
29487      * Returns the container element for this region.
29488      * @return {Roo.Element}
29489      */
29490     getEl : function(){
29491         return this.activePanel;
29492     },
29493     
29494     /**
29495      * Returns true if this region is currently visible.
29496      * @return {Boolean}
29497      */
29498     isVisible : function(){
29499         return this.activePanel ? true : false;
29500     },
29501     
29502     setActivePanel : function(panel){
29503         panel = this.getPanel(panel);
29504         if(this.activePanel && this.activePanel != panel){
29505             this.activePanel.setActiveState(false);
29506             this.activePanel.getEl().setLeftTop(-10000,-10000);
29507         }
29508         this.activePanel = panel;
29509         panel.setActiveState(true);
29510         if(this.box){
29511             panel.setSize(this.box.width, this.box.height);
29512         }
29513         this.fireEvent("panelactivated", this, panel);
29514         this.fireEvent("invalidated");
29515     },
29516     
29517     /**
29518      * Show the specified panel.
29519      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
29520      * @return {Roo.ContentPanel} The shown panel or null
29521      */
29522     showPanel : function(panel){
29523         if(panel = this.getPanel(panel)){
29524             this.setActivePanel(panel);
29525         }
29526         return panel;
29527     },
29528     
29529     /**
29530      * Get the active panel for this region.
29531      * @return {Roo.ContentPanel} The active panel or null
29532      */
29533     getActivePanel : function(){
29534         return this.activePanel;
29535     },
29536     
29537     /**
29538      * Add the passed ContentPanel(s)
29539      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
29540      * @return {Roo.ContentPanel} The panel added (if only one was added)
29541      */
29542     add : function(panel){
29543         if(arguments.length > 1){
29544             for(var i = 0, len = arguments.length; i < len; i++) {
29545                 this.add(arguments[i]);
29546             }
29547             return null;
29548         }
29549         if(this.hasPanel(panel)){
29550             this.showPanel(panel);
29551             return panel;
29552         }
29553         var el = panel.getEl();
29554         if(el.dom.parentNode != this.mgr.el.dom){
29555             this.mgr.el.dom.appendChild(el.dom);
29556         }
29557         if(panel.setRegion){
29558             panel.setRegion(this);
29559         }
29560         this.panels.add(panel);
29561         el.setStyle("position", "absolute");
29562         if(!panel.background){
29563             this.setActivePanel(panel);
29564             if(this.config.initialSize && this.panels.getCount()==1){
29565                 this.resizeTo(this.config.initialSize);
29566             }
29567         }
29568         this.fireEvent("paneladded", this, panel);
29569         return panel;
29570     },
29571     
29572     /**
29573      * Returns true if the panel is in this region.
29574      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29575      * @return {Boolean}
29576      */
29577     hasPanel : function(panel){
29578         if(typeof panel == "object"){ // must be panel obj
29579             panel = panel.getId();
29580         }
29581         return this.getPanel(panel) ? true : false;
29582     },
29583     
29584     /**
29585      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
29586      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29587      * @param {Boolean} preservePanel Overrides the config preservePanel option
29588      * @return {Roo.ContentPanel} The panel that was removed
29589      */
29590     remove : function(panel, preservePanel){
29591         panel = this.getPanel(panel);
29592         if(!panel){
29593             return null;
29594         }
29595         var e = {};
29596         this.fireEvent("beforeremove", this, panel, e);
29597         if(e.cancel === true){
29598             return null;
29599         }
29600         var panelId = panel.getId();
29601         this.panels.removeKey(panelId);
29602         return panel;
29603     },
29604     
29605     /**
29606      * Returns the panel specified or null if it's not in this region.
29607      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29608      * @return {Roo.ContentPanel}
29609      */
29610     getPanel : function(id){
29611         if(typeof id == "object"){ // must be panel obj
29612             return id;
29613         }
29614         return this.panels.get(id);
29615     },
29616     
29617     /**
29618      * Returns this regions position (north/south/east/west/center).
29619      * @return {String} 
29620      */
29621     getPosition: function(){
29622         return this.position;    
29623     }
29624 });/*
29625  * Based on:
29626  * Ext JS Library 1.1.1
29627  * Copyright(c) 2006-2007, Ext JS, LLC.
29628  *
29629  * Originally Released Under LGPL - original licence link has changed is not relivant.
29630  *
29631  * Fork - LGPL
29632  * <script type="text/javascript">
29633  */
29634  
29635 /**
29636  * @class Roo.LayoutRegion
29637  * @extends Roo.BasicLayoutRegion
29638  * This class represents a region in a layout manager.
29639  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
29640  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
29641  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
29642  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
29643  * @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})
29644  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
29645  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
29646  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
29647  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
29648  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
29649  * @cfg {String}    title           The title for the region (overrides panel titles)
29650  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
29651  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
29652  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
29653  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
29654  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
29655  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
29656  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
29657  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
29658  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
29659  * @cfg {Boolean}   showPin         True to show a pin button
29660  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
29661  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
29662  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
29663  * @cfg {Number}    width           For East/West panels
29664  * @cfg {Number}    height          For North/South panels
29665  * @cfg {Boolean}   split           To show the splitter
29666  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
29667  */
29668 Roo.LayoutRegion = function(mgr, config, pos){
29669     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
29670     var dh = Roo.DomHelper;
29671     /** This region's container element 
29672     * @type Roo.Element */
29673     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
29674     /** This region's title element 
29675     * @type Roo.Element */
29676
29677     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
29678         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
29679         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
29680     ]}, true);
29681     this.titleEl.enableDisplayMode();
29682     /** This region's title text element 
29683     * @type HTMLElement */
29684     this.titleTextEl = this.titleEl.dom.firstChild;
29685     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
29686     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
29687     this.closeBtn.enableDisplayMode();
29688     this.closeBtn.on("click", this.closeClicked, this);
29689     this.closeBtn.hide();
29690
29691     this.createBody(config);
29692     this.visible = true;
29693     this.collapsed = false;
29694
29695     if(config.hideWhenEmpty){
29696         this.hide();
29697         this.on("paneladded", this.validateVisibility, this);
29698         this.on("panelremoved", this.validateVisibility, this);
29699     }
29700     this.applyConfig(config);
29701 };
29702
29703 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
29704
29705     createBody : function(){
29706         /** This region's body element 
29707         * @type Roo.Element */
29708         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
29709     },
29710
29711     applyConfig : function(c){
29712         if(c.collapsible && this.position != "center" && !this.collapsedEl){
29713             var dh = Roo.DomHelper;
29714             if(c.titlebar !== false){
29715                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
29716                 this.collapseBtn.on("click", this.collapse, this);
29717                 this.collapseBtn.enableDisplayMode();
29718
29719                 if(c.showPin === true || this.showPin){
29720                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
29721                     this.stickBtn.enableDisplayMode();
29722                     this.stickBtn.on("click", this.expand, this);
29723                     this.stickBtn.hide();
29724                 }
29725             }
29726             /** This region's collapsed element
29727             * @type Roo.Element */
29728             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
29729                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
29730             ]}, true);
29731             if(c.floatable !== false){
29732                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
29733                this.collapsedEl.on("click", this.collapseClick, this);
29734             }
29735
29736             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
29737                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
29738                    id: "message", unselectable: "on", style:{"float":"left"}});
29739                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
29740              }
29741             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
29742             this.expandBtn.on("click", this.expand, this);
29743         }
29744         if(this.collapseBtn){
29745             this.collapseBtn.setVisible(c.collapsible == true);
29746         }
29747         this.cmargins = c.cmargins || this.cmargins ||
29748                          (this.position == "west" || this.position == "east" ?
29749                              {top: 0, left: 2, right:2, bottom: 0} :
29750                              {top: 2, left: 0, right:0, bottom: 2});
29751         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29752         this.bottomTabs = c.tabPosition != "top";
29753         this.autoScroll = c.autoScroll || false;
29754         if(this.autoScroll){
29755             this.bodyEl.setStyle("overflow", "auto");
29756         }else{
29757             this.bodyEl.setStyle("overflow", "hidden");
29758         }
29759         //if(c.titlebar !== false){
29760             if((!c.titlebar && !c.title) || c.titlebar === false){
29761                 this.titleEl.hide();
29762             }else{
29763                 this.titleEl.show();
29764                 if(c.title){
29765                     this.titleTextEl.innerHTML = c.title;
29766                 }
29767             }
29768         //}
29769         this.duration = c.duration || .30;
29770         this.slideDuration = c.slideDuration || .45;
29771         this.config = c;
29772         if(c.collapsed){
29773             this.collapse(true);
29774         }
29775         if(c.hidden){
29776             this.hide();
29777         }
29778     },
29779     /**
29780      * Returns true if this region is currently visible.
29781      * @return {Boolean}
29782      */
29783     isVisible : function(){
29784         return this.visible;
29785     },
29786
29787     /**
29788      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
29789      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
29790      */
29791     setCollapsedTitle : function(title){
29792         title = title || "&#160;";
29793         if(this.collapsedTitleTextEl){
29794             this.collapsedTitleTextEl.innerHTML = title;
29795         }
29796     },
29797
29798     getBox : function(){
29799         var b;
29800         if(!this.collapsed){
29801             b = this.el.getBox(false, true);
29802         }else{
29803             b = this.collapsedEl.getBox(false, true);
29804         }
29805         return b;
29806     },
29807
29808     getMargins : function(){
29809         return this.collapsed ? this.cmargins : this.margins;
29810     },
29811
29812     highlight : function(){
29813         this.el.addClass("x-layout-panel-dragover");
29814     },
29815
29816     unhighlight : function(){
29817         this.el.removeClass("x-layout-panel-dragover");
29818     },
29819
29820     updateBox : function(box){
29821         this.box = box;
29822         if(!this.collapsed){
29823             this.el.dom.style.left = box.x + "px";
29824             this.el.dom.style.top = box.y + "px";
29825             this.updateBody(box.width, box.height);
29826         }else{
29827             this.collapsedEl.dom.style.left = box.x + "px";
29828             this.collapsedEl.dom.style.top = box.y + "px";
29829             this.collapsedEl.setSize(box.width, box.height);
29830         }
29831         if(this.tabs){
29832             this.tabs.autoSizeTabs();
29833         }
29834     },
29835
29836     updateBody : function(w, h){
29837         if(w !== null){
29838             this.el.setWidth(w);
29839             w -= this.el.getBorderWidth("rl");
29840             if(this.config.adjustments){
29841                 w += this.config.adjustments[0];
29842             }
29843         }
29844         if(h !== null){
29845             this.el.setHeight(h);
29846             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
29847             h -= this.el.getBorderWidth("tb");
29848             if(this.config.adjustments){
29849                 h += this.config.adjustments[1];
29850             }
29851             this.bodyEl.setHeight(h);
29852             if(this.tabs){
29853                 h = this.tabs.syncHeight(h);
29854             }
29855         }
29856         if(this.panelSize){
29857             w = w !== null ? w : this.panelSize.width;
29858             h = h !== null ? h : this.panelSize.height;
29859         }
29860         if(this.activePanel){
29861             var el = this.activePanel.getEl();
29862             w = w !== null ? w : el.getWidth();
29863             h = h !== null ? h : el.getHeight();
29864             this.panelSize = {width: w, height: h};
29865             this.activePanel.setSize(w, h);
29866         }
29867         if(Roo.isIE && this.tabs){
29868             this.tabs.el.repaint();
29869         }
29870     },
29871
29872     /**
29873      * Returns the container element for this region.
29874      * @return {Roo.Element}
29875      */
29876     getEl : function(){
29877         return this.el;
29878     },
29879
29880     /**
29881      * Hides this region.
29882      */
29883     hide : function(){
29884         if(!this.collapsed){
29885             this.el.dom.style.left = "-2000px";
29886             this.el.hide();
29887         }else{
29888             this.collapsedEl.dom.style.left = "-2000px";
29889             this.collapsedEl.hide();
29890         }
29891         this.visible = false;
29892         this.fireEvent("visibilitychange", this, false);
29893     },
29894
29895     /**
29896      * Shows this region if it was previously hidden.
29897      */
29898     show : function(){
29899         if(!this.collapsed){
29900             this.el.show();
29901         }else{
29902             this.collapsedEl.show();
29903         }
29904         this.visible = true;
29905         this.fireEvent("visibilitychange", this, true);
29906     },
29907
29908     closeClicked : function(){
29909         if(this.activePanel){
29910             this.remove(this.activePanel);
29911         }
29912     },
29913
29914     collapseClick : function(e){
29915         if(this.isSlid){
29916            e.stopPropagation();
29917            this.slideIn();
29918         }else{
29919            e.stopPropagation();
29920            this.slideOut();
29921         }
29922     },
29923
29924     /**
29925      * Collapses this region.
29926      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
29927      */
29928     collapse : function(skipAnim, skipCheck = false){
29929         if(this.collapsed) {
29930             return;
29931         }
29932         
29933         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
29934             
29935             this.collapsed = true;
29936             if(this.split){
29937                 this.split.el.hide();
29938             }
29939             if(this.config.animate && skipAnim !== true){
29940                 this.fireEvent("invalidated", this);
29941                 this.animateCollapse();
29942             }else{
29943                 this.el.setLocation(-20000,-20000);
29944                 this.el.hide();
29945                 this.collapsedEl.show();
29946                 this.fireEvent("collapsed", this);
29947                 this.fireEvent("invalidated", this);
29948             }
29949         }
29950         
29951     },
29952
29953     animateCollapse : function(){
29954         // overridden
29955     },
29956
29957     /**
29958      * Expands this region if it was previously collapsed.
29959      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
29960      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
29961      */
29962     expand : function(e, skipAnim){
29963         if(e) {
29964             e.stopPropagation();
29965         }
29966         if(!this.collapsed || this.el.hasActiveFx()) {
29967             return;
29968         }
29969         if(this.isSlid){
29970             this.afterSlideIn();
29971             skipAnim = true;
29972         }
29973         this.collapsed = false;
29974         if(this.config.animate && skipAnim !== true){
29975             this.animateExpand();
29976         }else{
29977             this.el.show();
29978             if(this.split){
29979                 this.split.el.show();
29980             }
29981             this.collapsedEl.setLocation(-2000,-2000);
29982             this.collapsedEl.hide();
29983             this.fireEvent("invalidated", this);
29984             this.fireEvent("expanded", this);
29985         }
29986     },
29987
29988     animateExpand : function(){
29989         // overridden
29990     },
29991
29992     initTabs : function()
29993     {
29994         this.bodyEl.setStyle("overflow", "hidden");
29995         var ts = new Roo.TabPanel(
29996                 this.bodyEl.dom,
29997                 {
29998                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
29999                     disableTooltips: this.config.disableTabTips,
30000                     toolbar : this.config.toolbar
30001                 }
30002         );
30003         if(this.config.hideTabs){
30004             ts.stripWrap.setDisplayed(false);
30005         }
30006         this.tabs = ts;
30007         ts.resizeTabs = this.config.resizeTabs === true;
30008         ts.minTabWidth = this.config.minTabWidth || 40;
30009         ts.maxTabWidth = this.config.maxTabWidth || 250;
30010         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
30011         ts.monitorResize = false;
30012         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30013         ts.bodyEl.addClass('x-layout-tabs-body');
30014         this.panels.each(this.initPanelAsTab, this);
30015     },
30016
30017     initPanelAsTab : function(panel){
30018         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
30019                     this.config.closeOnTab && panel.isClosable());
30020         if(panel.tabTip !== undefined){
30021             ti.setTooltip(panel.tabTip);
30022         }
30023         ti.on("activate", function(){
30024               this.setActivePanel(panel);
30025         }, this);
30026         if(this.config.closeOnTab){
30027             ti.on("beforeclose", function(t, e){
30028                 e.cancel = true;
30029                 this.remove(panel);
30030             }, this);
30031         }
30032         return ti;
30033     },
30034
30035     updatePanelTitle : function(panel, title){
30036         if(this.activePanel == panel){
30037             this.updateTitle(title);
30038         }
30039         if(this.tabs){
30040             var ti = this.tabs.getTab(panel.getEl().id);
30041             ti.setText(title);
30042             if(panel.tabTip !== undefined){
30043                 ti.setTooltip(panel.tabTip);
30044             }
30045         }
30046     },
30047
30048     updateTitle : function(title){
30049         if(this.titleTextEl && !this.config.title){
30050             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
30051         }
30052     },
30053
30054     setActivePanel : function(panel){
30055         panel = this.getPanel(panel);
30056         if(this.activePanel && this.activePanel != panel){
30057             this.activePanel.setActiveState(false);
30058         }
30059         this.activePanel = panel;
30060         panel.setActiveState(true);
30061         if(this.panelSize){
30062             panel.setSize(this.panelSize.width, this.panelSize.height);
30063         }
30064         if(this.closeBtn){
30065             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
30066         }
30067         this.updateTitle(panel.getTitle());
30068         if(this.tabs){
30069             this.fireEvent("invalidated", this);
30070         }
30071         this.fireEvent("panelactivated", this, panel);
30072     },
30073
30074     /**
30075      * Shows the specified panel.
30076      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
30077      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
30078      */
30079     showPanel : function(panel)
30080     {
30081         panel = this.getPanel(panel);
30082         if(panel){
30083             if(this.tabs){
30084                 var tab = this.tabs.getTab(panel.getEl().id);
30085                 if(tab.isHidden()){
30086                     this.tabs.unhideTab(tab.id);
30087                 }
30088                 tab.activate();
30089             }else{
30090                 this.setActivePanel(panel);
30091             }
30092         }
30093         return panel;
30094     },
30095
30096     /**
30097      * Get the active panel for this region.
30098      * @return {Roo.ContentPanel} The active panel or null
30099      */
30100     getActivePanel : function(){
30101         return this.activePanel;
30102     },
30103
30104     validateVisibility : function(){
30105         if(this.panels.getCount() < 1){
30106             this.updateTitle("&#160;");
30107             this.closeBtn.hide();
30108             this.hide();
30109         }else{
30110             if(!this.isVisible()){
30111                 this.show();
30112             }
30113         }
30114     },
30115
30116     /**
30117      * Adds the passed ContentPanel(s) to this region.
30118      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
30119      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
30120      */
30121     add : function(panel){
30122         if(arguments.length > 1){
30123             for(var i = 0, len = arguments.length; i < len; i++) {
30124                 this.add(arguments[i]);
30125             }
30126             return null;
30127         }
30128         if(this.hasPanel(panel)){
30129             this.showPanel(panel);
30130             return panel;
30131         }
30132         panel.setRegion(this);
30133         this.panels.add(panel);
30134         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
30135             this.bodyEl.dom.appendChild(panel.getEl().dom);
30136             if(panel.background !== true){
30137                 this.setActivePanel(panel);
30138             }
30139             this.fireEvent("paneladded", this, panel);
30140             return panel;
30141         }
30142         if(!this.tabs){
30143             this.initTabs();
30144         }else{
30145             this.initPanelAsTab(panel);
30146         }
30147         if(panel.background !== true){
30148             this.tabs.activate(panel.getEl().id);
30149         }
30150         this.fireEvent("paneladded", this, panel);
30151         return panel;
30152     },
30153
30154     /**
30155      * Hides the tab for the specified panel.
30156      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30157      */
30158     hidePanel : function(panel){
30159         if(this.tabs && (panel = this.getPanel(panel))){
30160             this.tabs.hideTab(panel.getEl().id);
30161         }
30162     },
30163
30164     /**
30165      * Unhides the tab for a previously hidden panel.
30166      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30167      */
30168     unhidePanel : function(panel){
30169         if(this.tabs && (panel = this.getPanel(panel))){
30170             this.tabs.unhideTab(panel.getEl().id);
30171         }
30172     },
30173
30174     clearPanels : function(){
30175         while(this.panels.getCount() > 0){
30176              this.remove(this.panels.first());
30177         }
30178     },
30179
30180     /**
30181      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30182      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30183      * @param {Boolean} preservePanel Overrides the config preservePanel option
30184      * @return {Roo.ContentPanel} The panel that was removed
30185      */
30186     remove : function(panel, preservePanel){
30187         panel = this.getPanel(panel);
30188         if(!panel){
30189             return null;
30190         }
30191         var e = {};
30192         this.fireEvent("beforeremove", this, panel, e);
30193         if(e.cancel === true){
30194             return null;
30195         }
30196         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
30197         var panelId = panel.getId();
30198         this.panels.removeKey(panelId);
30199         if(preservePanel){
30200             document.body.appendChild(panel.getEl().dom);
30201         }
30202         if(this.tabs){
30203             this.tabs.removeTab(panel.getEl().id);
30204         }else if (!preservePanel){
30205             this.bodyEl.dom.removeChild(panel.getEl().dom);
30206         }
30207         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
30208             var p = this.panels.first();
30209             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
30210             tempEl.appendChild(p.getEl().dom);
30211             this.bodyEl.update("");
30212             this.bodyEl.dom.appendChild(p.getEl().dom);
30213             tempEl = null;
30214             this.updateTitle(p.getTitle());
30215             this.tabs = null;
30216             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30217             this.setActivePanel(p);
30218         }
30219         panel.setRegion(null);
30220         if(this.activePanel == panel){
30221             this.activePanel = null;
30222         }
30223         if(this.config.autoDestroy !== false && preservePanel !== true){
30224             try{panel.destroy();}catch(e){}
30225         }
30226         this.fireEvent("panelremoved", this, panel);
30227         return panel;
30228     },
30229
30230     /**
30231      * Returns the TabPanel component used by this region
30232      * @return {Roo.TabPanel}
30233      */
30234     getTabs : function(){
30235         return this.tabs;
30236     },
30237
30238     createTool : function(parentEl, className){
30239         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
30240             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
30241         btn.addClassOnOver("x-layout-tools-button-over");
30242         return btn;
30243     }
30244 });/*
30245  * Based on:
30246  * Ext JS Library 1.1.1
30247  * Copyright(c) 2006-2007, Ext JS, LLC.
30248  *
30249  * Originally Released Under LGPL - original licence link has changed is not relivant.
30250  *
30251  * Fork - LGPL
30252  * <script type="text/javascript">
30253  */
30254  
30255
30256
30257 /**
30258  * @class Roo.SplitLayoutRegion
30259  * @extends Roo.LayoutRegion
30260  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
30261  */
30262 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
30263     this.cursor = cursor;
30264     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
30265 };
30266
30267 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
30268     splitTip : "Drag to resize.",
30269     collapsibleSplitTip : "Drag to resize. Double click to hide.",
30270     useSplitTips : false,
30271
30272     applyConfig : function(config){
30273         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
30274         if(config.split){
30275             if(!this.split){
30276                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
30277                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
30278                 /** The SplitBar for this region 
30279                 * @type Roo.SplitBar */
30280                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
30281                 this.split.on("moved", this.onSplitMove, this);
30282                 this.split.useShim = config.useShim === true;
30283                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
30284                 if(this.useSplitTips){
30285                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
30286                 }
30287                 if(config.collapsible){
30288                     this.split.el.on("dblclick", this.collapse,  this);
30289                 }
30290             }
30291             if(typeof config.minSize != "undefined"){
30292                 this.split.minSize = config.minSize;
30293             }
30294             if(typeof config.maxSize != "undefined"){
30295                 this.split.maxSize = config.maxSize;
30296             }
30297             if(config.hideWhenEmpty || config.hidden || config.collapsed){
30298                 this.hideSplitter();
30299             }
30300         }
30301     },
30302
30303     getHMaxSize : function(){
30304          var cmax = this.config.maxSize || 10000;
30305          var center = this.mgr.getRegion("center");
30306          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
30307     },
30308
30309     getVMaxSize : function(){
30310          var cmax = this.config.maxSize || 10000;
30311          var center = this.mgr.getRegion("center");
30312          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
30313     },
30314
30315     onSplitMove : function(split, newSize){
30316         this.fireEvent("resized", this, newSize);
30317     },
30318     
30319     /** 
30320      * Returns the {@link Roo.SplitBar} for this region.
30321      * @return {Roo.SplitBar}
30322      */
30323     getSplitBar : function(){
30324         return this.split;
30325     },
30326     
30327     hide : function(){
30328         this.hideSplitter();
30329         Roo.SplitLayoutRegion.superclass.hide.call(this);
30330     },
30331
30332     hideSplitter : function(){
30333         if(this.split){
30334             this.split.el.setLocation(-2000,-2000);
30335             this.split.el.hide();
30336         }
30337     },
30338
30339     show : function(){
30340         if(this.split){
30341             this.split.el.show();
30342         }
30343         Roo.SplitLayoutRegion.superclass.show.call(this);
30344     },
30345     
30346     beforeSlide: function(){
30347         if(Roo.isGecko){// firefox overflow auto bug workaround
30348             this.bodyEl.clip();
30349             if(this.tabs) {
30350                 this.tabs.bodyEl.clip();
30351             }
30352             if(this.activePanel){
30353                 this.activePanel.getEl().clip();
30354                 
30355                 if(this.activePanel.beforeSlide){
30356                     this.activePanel.beforeSlide();
30357                 }
30358             }
30359         }
30360     },
30361     
30362     afterSlide : function(){
30363         if(Roo.isGecko){// firefox overflow auto bug workaround
30364             this.bodyEl.unclip();
30365             if(this.tabs) {
30366                 this.tabs.bodyEl.unclip();
30367             }
30368             if(this.activePanel){
30369                 this.activePanel.getEl().unclip();
30370                 if(this.activePanel.afterSlide){
30371                     this.activePanel.afterSlide();
30372                 }
30373             }
30374         }
30375     },
30376
30377     initAutoHide : function(){
30378         if(this.autoHide !== false){
30379             if(!this.autoHideHd){
30380                 var st = new Roo.util.DelayedTask(this.slideIn, this);
30381                 this.autoHideHd = {
30382                     "mouseout": function(e){
30383                         if(!e.within(this.el, true)){
30384                             st.delay(500);
30385                         }
30386                     },
30387                     "mouseover" : function(e){
30388                         st.cancel();
30389                     },
30390                     scope : this
30391                 };
30392             }
30393             this.el.on(this.autoHideHd);
30394         }
30395     },
30396
30397     clearAutoHide : function(){
30398         if(this.autoHide !== false){
30399             this.el.un("mouseout", this.autoHideHd.mouseout);
30400             this.el.un("mouseover", this.autoHideHd.mouseover);
30401         }
30402     },
30403
30404     clearMonitor : function(){
30405         Roo.get(document).un("click", this.slideInIf, this);
30406     },
30407
30408     // these names are backwards but not changed for compat
30409     slideOut : function(){
30410         if(this.isSlid || this.el.hasActiveFx()){
30411             return;
30412         }
30413         this.isSlid = true;
30414         if(this.collapseBtn){
30415             this.collapseBtn.hide();
30416         }
30417         this.closeBtnState = this.closeBtn.getStyle('display');
30418         this.closeBtn.hide();
30419         if(this.stickBtn){
30420             this.stickBtn.show();
30421         }
30422         this.el.show();
30423         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
30424         this.beforeSlide();
30425         this.el.setStyle("z-index", 10001);
30426         this.el.slideIn(this.getSlideAnchor(), {
30427             callback: function(){
30428                 this.afterSlide();
30429                 this.initAutoHide();
30430                 Roo.get(document).on("click", this.slideInIf, this);
30431                 this.fireEvent("slideshow", this);
30432             },
30433             scope: this,
30434             block: true
30435         });
30436     },
30437
30438     afterSlideIn : function(){
30439         this.clearAutoHide();
30440         this.isSlid = false;
30441         this.clearMonitor();
30442         this.el.setStyle("z-index", "");
30443         if(this.collapseBtn){
30444             this.collapseBtn.show();
30445         }
30446         this.closeBtn.setStyle('display', this.closeBtnState);
30447         if(this.stickBtn){
30448             this.stickBtn.hide();
30449         }
30450         this.fireEvent("slidehide", this);
30451     },
30452
30453     slideIn : function(cb){
30454         if(!this.isSlid || this.el.hasActiveFx()){
30455             Roo.callback(cb);
30456             return;
30457         }
30458         this.isSlid = false;
30459         this.beforeSlide();
30460         this.el.slideOut(this.getSlideAnchor(), {
30461             callback: function(){
30462                 this.el.setLeftTop(-10000, -10000);
30463                 this.afterSlide();
30464                 this.afterSlideIn();
30465                 Roo.callback(cb);
30466             },
30467             scope: this,
30468             block: true
30469         });
30470     },
30471     
30472     slideInIf : function(e){
30473         if(!e.within(this.el)){
30474             this.slideIn();
30475         }
30476     },
30477
30478     animateCollapse : function(){
30479         this.beforeSlide();
30480         this.el.setStyle("z-index", 20000);
30481         var anchor = this.getSlideAnchor();
30482         this.el.slideOut(anchor, {
30483             callback : function(){
30484                 this.el.setStyle("z-index", "");
30485                 this.collapsedEl.slideIn(anchor, {duration:.3});
30486                 this.afterSlide();
30487                 this.el.setLocation(-10000,-10000);
30488                 this.el.hide();
30489                 this.fireEvent("collapsed", this);
30490             },
30491             scope: this,
30492             block: true
30493         });
30494     },
30495
30496     animateExpand : function(){
30497         this.beforeSlide();
30498         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
30499         this.el.setStyle("z-index", 20000);
30500         this.collapsedEl.hide({
30501             duration:.1
30502         });
30503         this.el.slideIn(this.getSlideAnchor(), {
30504             callback : function(){
30505                 this.el.setStyle("z-index", "");
30506                 this.afterSlide();
30507                 if(this.split){
30508                     this.split.el.show();
30509                 }
30510                 this.fireEvent("invalidated", this);
30511                 this.fireEvent("expanded", this);
30512             },
30513             scope: this,
30514             block: true
30515         });
30516     },
30517
30518     anchors : {
30519         "west" : "left",
30520         "east" : "right",
30521         "north" : "top",
30522         "south" : "bottom"
30523     },
30524
30525     sanchors : {
30526         "west" : "l",
30527         "east" : "r",
30528         "north" : "t",
30529         "south" : "b"
30530     },
30531
30532     canchors : {
30533         "west" : "tl-tr",
30534         "east" : "tr-tl",
30535         "north" : "tl-bl",
30536         "south" : "bl-tl"
30537     },
30538
30539     getAnchor : function(){
30540         return this.anchors[this.position];
30541     },
30542
30543     getCollapseAnchor : function(){
30544         return this.canchors[this.position];
30545     },
30546
30547     getSlideAnchor : function(){
30548         return this.sanchors[this.position];
30549     },
30550
30551     getAlignAdj : function(){
30552         var cm = this.cmargins;
30553         switch(this.position){
30554             case "west":
30555                 return [0, 0];
30556             break;
30557             case "east":
30558                 return [0, 0];
30559             break;
30560             case "north":
30561                 return [0, 0];
30562             break;
30563             case "south":
30564                 return [0, 0];
30565             break;
30566         }
30567     },
30568
30569     getExpandAdj : function(){
30570         var c = this.collapsedEl, cm = this.cmargins;
30571         switch(this.position){
30572             case "west":
30573                 return [-(cm.right+c.getWidth()+cm.left), 0];
30574             break;
30575             case "east":
30576                 return [cm.right+c.getWidth()+cm.left, 0];
30577             break;
30578             case "north":
30579                 return [0, -(cm.top+cm.bottom+c.getHeight())];
30580             break;
30581             case "south":
30582                 return [0, cm.top+cm.bottom+c.getHeight()];
30583             break;
30584         }
30585     }
30586 });/*
30587  * Based on:
30588  * Ext JS Library 1.1.1
30589  * Copyright(c) 2006-2007, Ext JS, LLC.
30590  *
30591  * Originally Released Under LGPL - original licence link has changed is not relivant.
30592  *
30593  * Fork - LGPL
30594  * <script type="text/javascript">
30595  */
30596 /*
30597  * These classes are private internal classes
30598  */
30599 Roo.CenterLayoutRegion = function(mgr, config){
30600     Roo.LayoutRegion.call(this, mgr, config, "center");
30601     this.visible = true;
30602     this.minWidth = config.minWidth || 20;
30603     this.minHeight = config.minHeight || 20;
30604 };
30605
30606 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
30607     hide : function(){
30608         // center panel can't be hidden
30609     },
30610     
30611     show : function(){
30612         // center panel can't be hidden
30613     },
30614     
30615     getMinWidth: function(){
30616         return this.minWidth;
30617     },
30618     
30619     getMinHeight: function(){
30620         return this.minHeight;
30621     }
30622 });
30623
30624
30625 Roo.NorthLayoutRegion = function(mgr, config){
30626     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
30627     if(this.split){
30628         this.split.placement = Roo.SplitBar.TOP;
30629         this.split.orientation = Roo.SplitBar.VERTICAL;
30630         this.split.el.addClass("x-layout-split-v");
30631     }
30632     var size = config.initialSize || config.height;
30633     if(typeof size != "undefined"){
30634         this.el.setHeight(size);
30635     }
30636 };
30637 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
30638     orientation: Roo.SplitBar.VERTICAL,
30639     getBox : function(){
30640         if(this.collapsed){
30641             return this.collapsedEl.getBox();
30642         }
30643         var box = this.el.getBox();
30644         if(this.split){
30645             box.height += this.split.el.getHeight();
30646         }
30647         return box;
30648     },
30649     
30650     updateBox : function(box){
30651         if(this.split && !this.collapsed){
30652             box.height -= this.split.el.getHeight();
30653             this.split.el.setLeft(box.x);
30654             this.split.el.setTop(box.y+box.height);
30655             this.split.el.setWidth(box.width);
30656         }
30657         if(this.collapsed){
30658             this.updateBody(box.width, null);
30659         }
30660         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30661     }
30662 });
30663
30664 Roo.SouthLayoutRegion = function(mgr, config){
30665     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
30666     if(this.split){
30667         this.split.placement = Roo.SplitBar.BOTTOM;
30668         this.split.orientation = Roo.SplitBar.VERTICAL;
30669         this.split.el.addClass("x-layout-split-v");
30670     }
30671     var size = config.initialSize || config.height;
30672     if(typeof size != "undefined"){
30673         this.el.setHeight(size);
30674     }
30675 };
30676 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
30677     orientation: Roo.SplitBar.VERTICAL,
30678     getBox : function(){
30679         if(this.collapsed){
30680             return this.collapsedEl.getBox();
30681         }
30682         var box = this.el.getBox();
30683         if(this.split){
30684             var sh = this.split.el.getHeight();
30685             box.height += sh;
30686             box.y -= sh;
30687         }
30688         return box;
30689     },
30690     
30691     updateBox : function(box){
30692         if(this.split && !this.collapsed){
30693             var sh = this.split.el.getHeight();
30694             box.height -= sh;
30695             box.y += sh;
30696             this.split.el.setLeft(box.x);
30697             this.split.el.setTop(box.y-sh);
30698             this.split.el.setWidth(box.width);
30699         }
30700         if(this.collapsed){
30701             this.updateBody(box.width, null);
30702         }
30703         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30704     }
30705 });
30706
30707 Roo.EastLayoutRegion = function(mgr, config){
30708     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
30709     if(this.split){
30710         this.split.placement = Roo.SplitBar.RIGHT;
30711         this.split.orientation = Roo.SplitBar.HORIZONTAL;
30712         this.split.el.addClass("x-layout-split-h");
30713     }
30714     var size = config.initialSize || config.width;
30715     if(typeof size != "undefined"){
30716         this.el.setWidth(size);
30717     }
30718 };
30719 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
30720     orientation: Roo.SplitBar.HORIZONTAL,
30721     getBox : function(){
30722         if(this.collapsed){
30723             return this.collapsedEl.getBox();
30724         }
30725         var box = this.el.getBox();
30726         if(this.split){
30727             var sw = this.split.el.getWidth();
30728             box.width += sw;
30729             box.x -= sw;
30730         }
30731         return box;
30732     },
30733
30734     updateBox : function(box){
30735         if(this.split && !this.collapsed){
30736             var sw = this.split.el.getWidth();
30737             box.width -= sw;
30738             this.split.el.setLeft(box.x);
30739             this.split.el.setTop(box.y);
30740             this.split.el.setHeight(box.height);
30741             box.x += sw;
30742         }
30743         if(this.collapsed){
30744             this.updateBody(null, box.height);
30745         }
30746         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30747     }
30748 });
30749
30750 Roo.WestLayoutRegion = function(mgr, config){
30751     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
30752     if(this.split){
30753         this.split.placement = Roo.SplitBar.LEFT;
30754         this.split.orientation = Roo.SplitBar.HORIZONTAL;
30755         this.split.el.addClass("x-layout-split-h");
30756     }
30757     var size = config.initialSize || config.width;
30758     if(typeof size != "undefined"){
30759         this.el.setWidth(size);
30760     }
30761 };
30762 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
30763     orientation: Roo.SplitBar.HORIZONTAL,
30764     getBox : function(){
30765         if(this.collapsed){
30766             return this.collapsedEl.getBox();
30767         }
30768         var box = this.el.getBox();
30769         if(this.split){
30770             box.width += this.split.el.getWidth();
30771         }
30772         return box;
30773     },
30774     
30775     updateBox : function(box){
30776         if(this.split && !this.collapsed){
30777             var sw = this.split.el.getWidth();
30778             box.width -= sw;
30779             this.split.el.setLeft(box.x+box.width);
30780             this.split.el.setTop(box.y);
30781             this.split.el.setHeight(box.height);
30782         }
30783         if(this.collapsed){
30784             this.updateBody(null, box.height);
30785         }
30786         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30787     }
30788 });
30789 /*
30790  * Based on:
30791  * Ext JS Library 1.1.1
30792  * Copyright(c) 2006-2007, Ext JS, LLC.
30793  *
30794  * Originally Released Under LGPL - original licence link has changed is not relivant.
30795  *
30796  * Fork - LGPL
30797  * <script type="text/javascript">
30798  */
30799  
30800  
30801 /*
30802  * Private internal class for reading and applying state
30803  */
30804 Roo.LayoutStateManager = function(layout){
30805      // default empty state
30806      this.state = {
30807         north: {},
30808         south: {},
30809         east: {},
30810         west: {}       
30811     };
30812 };
30813
30814 Roo.LayoutStateManager.prototype = {
30815     init : function(layout, provider){
30816         this.provider = provider;
30817         var state = provider.get(layout.id+"-layout-state");
30818         if(state){
30819             var wasUpdating = layout.isUpdating();
30820             if(!wasUpdating){
30821                 layout.beginUpdate();
30822             }
30823             for(var key in state){
30824                 if(typeof state[key] != "function"){
30825                     var rstate = state[key];
30826                     var r = layout.getRegion(key);
30827                     if(r && rstate){
30828                         if(rstate.size){
30829                             r.resizeTo(rstate.size);
30830                         }
30831                         if(rstate.collapsed == true){
30832                             r.collapse(true);
30833                         }else{
30834                             r.expand(null, true);
30835                         }
30836                     }
30837                 }
30838             }
30839             if(!wasUpdating){
30840                 layout.endUpdate();
30841             }
30842             this.state = state; 
30843         }
30844         this.layout = layout;
30845         layout.on("regionresized", this.onRegionResized, this);
30846         layout.on("regioncollapsed", this.onRegionCollapsed, this);
30847         layout.on("regionexpanded", this.onRegionExpanded, this);
30848     },
30849     
30850     storeState : function(){
30851         this.provider.set(this.layout.id+"-layout-state", this.state);
30852     },
30853     
30854     onRegionResized : function(region, newSize){
30855         this.state[region.getPosition()].size = newSize;
30856         this.storeState();
30857     },
30858     
30859     onRegionCollapsed : function(region){
30860         this.state[region.getPosition()].collapsed = true;
30861         this.storeState();
30862     },
30863     
30864     onRegionExpanded : function(region){
30865         this.state[region.getPosition()].collapsed = false;
30866         this.storeState();
30867     }
30868 };/*
30869  * Based on:
30870  * Ext JS Library 1.1.1
30871  * Copyright(c) 2006-2007, Ext JS, LLC.
30872  *
30873  * Originally Released Under LGPL - original licence link has changed is not relivant.
30874  *
30875  * Fork - LGPL
30876  * <script type="text/javascript">
30877  */
30878 /**
30879  * @class Roo.ContentPanel
30880  * @extends Roo.util.Observable
30881  * A basic ContentPanel element.
30882  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
30883  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
30884  * @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
30885  * @cfg {Boolean}   closable      True if the panel can be closed/removed
30886  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
30887  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
30888  * @cfg {Toolbar}   toolbar       A toolbar for this panel
30889  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
30890  * @cfg {String} title          The title for this panel
30891  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
30892  * @cfg {String} url            Calls {@link #setUrl} with this value
30893  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
30894  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
30895  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
30896  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
30897
30898  * @constructor
30899  * Create a new ContentPanel.
30900  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
30901  * @param {String/Object} config A string to set only the title or a config object
30902  * @param {String} content (optional) Set the HTML content for this panel
30903  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
30904  */
30905 Roo.ContentPanel = function(el, config, content){
30906     
30907      
30908     /*
30909     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
30910         config = el;
30911         el = Roo.id();
30912     }
30913     if (config && config.parentLayout) { 
30914         el = config.parentLayout.el.createChild(); 
30915     }
30916     */
30917     if(el.autoCreate){ // xtype is available if this is called from factory
30918         config = el;
30919         el = Roo.id();
30920     }
30921     this.el = Roo.get(el);
30922     if(!this.el && config && config.autoCreate){
30923         if(typeof config.autoCreate == "object"){
30924             if(!config.autoCreate.id){
30925                 config.autoCreate.id = config.id||el;
30926             }
30927             this.el = Roo.DomHelper.append(document.body,
30928                         config.autoCreate, true);
30929         }else{
30930             this.el = Roo.DomHelper.append(document.body,
30931                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
30932         }
30933     }
30934     this.closable = false;
30935     this.loaded = false;
30936     this.active = false;
30937     if(typeof config == "string"){
30938         this.title = config;
30939     }else{
30940         Roo.apply(this, config);
30941     }
30942     
30943     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
30944         this.wrapEl = this.el.wrap();
30945         this.toolbar.container = this.el.insertSibling(false, 'before');
30946         this.toolbar = new Roo.Toolbar(this.toolbar);
30947     }
30948     
30949     // xtype created footer. - not sure if will work as we normally have to render first..
30950     if (this.footer && !this.footer.el && this.footer.xtype) {
30951         if (!this.wrapEl) {
30952             this.wrapEl = this.el.wrap();
30953         }
30954     
30955         this.footer.container = this.wrapEl.createChild();
30956          
30957         this.footer = Roo.factory(this.footer, Roo);
30958         
30959     }
30960     
30961     if(this.resizeEl){
30962         this.resizeEl = Roo.get(this.resizeEl, true);
30963     }else{
30964         this.resizeEl = this.el;
30965     }
30966     // handle view.xtype
30967     
30968  
30969     
30970     
30971     this.addEvents({
30972         /**
30973          * @event activate
30974          * Fires when this panel is activated. 
30975          * @param {Roo.ContentPanel} this
30976          */
30977         "activate" : true,
30978         /**
30979          * @event deactivate
30980          * Fires when this panel is activated. 
30981          * @param {Roo.ContentPanel} this
30982          */
30983         "deactivate" : true,
30984
30985         /**
30986          * @event resize
30987          * Fires when this panel is resized if fitToFrame is true.
30988          * @param {Roo.ContentPanel} this
30989          * @param {Number} width The width after any component adjustments
30990          * @param {Number} height The height after any component adjustments
30991          */
30992         "resize" : true,
30993         
30994          /**
30995          * @event render
30996          * Fires when this tab is created
30997          * @param {Roo.ContentPanel} this
30998          */
30999         "render" : true
31000          
31001         
31002     });
31003     
31004
31005     
31006     
31007     if(this.autoScroll){
31008         this.resizeEl.setStyle("overflow", "auto");
31009     } else {
31010         // fix randome scrolling
31011         this.el.on('scroll', function() {
31012             Roo.log('fix random scolling');
31013             this.scrollTo('top',0); 
31014         });
31015     }
31016     content = content || this.content;
31017     if(content){
31018         this.setContent(content);
31019     }
31020     if(config && config.url){
31021         this.setUrl(this.url, this.params, this.loadOnce);
31022     }
31023     
31024     
31025     
31026     Roo.ContentPanel.superclass.constructor.call(this);
31027     
31028     if (this.view && typeof(this.view.xtype) != 'undefined') {
31029         this.view.el = this.el.appendChild(document.createElement("div"));
31030         this.view = Roo.factory(this.view); 
31031         this.view.render  &&  this.view.render(false, '');  
31032     }
31033     
31034     
31035     this.fireEvent('render', this);
31036 };
31037
31038 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
31039     tabTip:'',
31040     setRegion : function(region){
31041         this.region = region;
31042         if(region){
31043            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
31044         }else{
31045            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
31046         } 
31047     },
31048     
31049     /**
31050      * Returns the toolbar for this Panel if one was configured. 
31051      * @return {Roo.Toolbar} 
31052      */
31053     getToolbar : function(){
31054         return this.toolbar;
31055     },
31056     
31057     setActiveState : function(active){
31058         this.active = active;
31059         if(!active){
31060             this.fireEvent("deactivate", this);
31061         }else{
31062             this.fireEvent("activate", this);
31063         }
31064     },
31065     /**
31066      * Updates this panel's element
31067      * @param {String} content The new content
31068      * @param {Boolean} loadScripts (optional) true to look for and process scripts
31069     */
31070     setContent : function(content, loadScripts){
31071         this.el.update(content, loadScripts);
31072     },
31073
31074     ignoreResize : function(w, h){
31075         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
31076             return true;
31077         }else{
31078             this.lastSize = {width: w, height: h};
31079             return false;
31080         }
31081     },
31082     /**
31083      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
31084      * @return {Roo.UpdateManager} The UpdateManager
31085      */
31086     getUpdateManager : function(){
31087         return this.el.getUpdateManager();
31088     },
31089      /**
31090      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
31091      * @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:
31092 <pre><code>
31093 panel.load({
31094     url: "your-url.php",
31095     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
31096     callback: yourFunction,
31097     scope: yourObject, //(optional scope)
31098     discardUrl: false,
31099     nocache: false,
31100     text: "Loading...",
31101     timeout: 30,
31102     scripts: false
31103 });
31104 </code></pre>
31105      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
31106      * 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.
31107      * @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}
31108      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
31109      * @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.
31110      * @return {Roo.ContentPanel} this
31111      */
31112     load : function(){
31113         var um = this.el.getUpdateManager();
31114         um.update.apply(um, arguments);
31115         return this;
31116     },
31117
31118
31119     /**
31120      * 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.
31121      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
31122      * @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)
31123      * @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)
31124      * @return {Roo.UpdateManager} The UpdateManager
31125      */
31126     setUrl : function(url, params, loadOnce){
31127         if(this.refreshDelegate){
31128             this.removeListener("activate", this.refreshDelegate);
31129         }
31130         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
31131         this.on("activate", this.refreshDelegate);
31132         return this.el.getUpdateManager();
31133     },
31134     
31135     _handleRefresh : function(url, params, loadOnce){
31136         if(!loadOnce || !this.loaded){
31137             var updater = this.el.getUpdateManager();
31138             updater.update(url, params, this._setLoaded.createDelegate(this));
31139         }
31140     },
31141     
31142     _setLoaded : function(){
31143         this.loaded = true;
31144     }, 
31145     
31146     /**
31147      * Returns this panel's id
31148      * @return {String} 
31149      */
31150     getId : function(){
31151         return this.el.id;
31152     },
31153     
31154     /** 
31155      * Returns this panel's element - used by regiosn to add.
31156      * @return {Roo.Element} 
31157      */
31158     getEl : function(){
31159         return this.wrapEl || this.el;
31160     },
31161     
31162     adjustForComponents : function(width, height)
31163     {
31164         //Roo.log('adjustForComponents ');
31165         if(this.resizeEl != this.el){
31166             width -= this.el.getFrameWidth('lr');
31167             height -= this.el.getFrameWidth('tb');
31168         }
31169         if(this.toolbar){
31170             var te = this.toolbar.getEl();
31171             height -= te.getHeight();
31172             te.setWidth(width);
31173         }
31174         if(this.footer){
31175             var te = this.footer.getEl();
31176             //Roo.log("footer:" + te.getHeight());
31177             
31178             height -= te.getHeight();
31179             te.setWidth(width);
31180         }
31181         
31182         
31183         if(this.adjustments){
31184             width += this.adjustments[0];
31185             height += this.adjustments[1];
31186         }
31187         return {"width": width, "height": height};
31188     },
31189     
31190     setSize : function(width, height){
31191         if(this.fitToFrame && !this.ignoreResize(width, height)){
31192             if(this.fitContainer && this.resizeEl != this.el){
31193                 this.el.setSize(width, height);
31194             }
31195             var size = this.adjustForComponents(width, height);
31196             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
31197             this.fireEvent('resize', this, size.width, size.height);
31198         }
31199     },
31200     
31201     /**
31202      * Returns this panel's title
31203      * @return {String} 
31204      */
31205     getTitle : function(){
31206         return this.title;
31207     },
31208     
31209     /**
31210      * Set this panel's title
31211      * @param {String} title
31212      */
31213     setTitle : function(title){
31214         this.title = title;
31215         if(this.region){
31216             this.region.updatePanelTitle(this, title);
31217         }
31218     },
31219     
31220     /**
31221      * Returns true is this panel was configured to be closable
31222      * @return {Boolean} 
31223      */
31224     isClosable : function(){
31225         return this.closable;
31226     },
31227     
31228     beforeSlide : function(){
31229         this.el.clip();
31230         this.resizeEl.clip();
31231     },
31232     
31233     afterSlide : function(){
31234         this.el.unclip();
31235         this.resizeEl.unclip();
31236     },
31237     
31238     /**
31239      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
31240      *   Will fail silently if the {@link #setUrl} method has not been called.
31241      *   This does not activate the panel, just updates its content.
31242      */
31243     refresh : function(){
31244         if(this.refreshDelegate){
31245            this.loaded = false;
31246            this.refreshDelegate();
31247         }
31248     },
31249     
31250     /**
31251      * Destroys this panel
31252      */
31253     destroy : function(){
31254         this.el.removeAllListeners();
31255         var tempEl = document.createElement("span");
31256         tempEl.appendChild(this.el.dom);
31257         tempEl.innerHTML = "";
31258         this.el.remove();
31259         this.el = null;
31260     },
31261     
31262     /**
31263      * form - if the content panel contains a form - this is a reference to it.
31264      * @type {Roo.form.Form}
31265      */
31266     form : false,
31267     /**
31268      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
31269      *    This contains a reference to it.
31270      * @type {Roo.View}
31271      */
31272     view : false,
31273     
31274       /**
31275      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
31276      * <pre><code>
31277
31278 layout.addxtype({
31279        xtype : 'Form',
31280        items: [ .... ]
31281    }
31282 );
31283
31284 </code></pre>
31285      * @param {Object} cfg Xtype definition of item to add.
31286      */
31287     
31288     addxtype : function(cfg) {
31289         // add form..
31290         if (cfg.xtype.match(/^Form$/)) {
31291             
31292             var el;
31293             //if (this.footer) {
31294             //    el = this.footer.container.insertSibling(false, 'before');
31295             //} else {
31296                 el = this.el.createChild();
31297             //}
31298
31299             this.form = new  Roo.form.Form(cfg);
31300             
31301             
31302             if ( this.form.allItems.length) {
31303                 this.form.render(el.dom);
31304             }
31305             return this.form;
31306         }
31307         // should only have one of theses..
31308         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
31309             // views.. should not be just added - used named prop 'view''
31310             
31311             cfg.el = this.el.appendChild(document.createElement("div"));
31312             // factory?
31313             
31314             var ret = new Roo.factory(cfg);
31315              
31316              ret.render && ret.render(false, ''); // render blank..
31317             this.view = ret;
31318             return ret;
31319         }
31320         return false;
31321     }
31322 });
31323
31324 /**
31325  * @class Roo.GridPanel
31326  * @extends Roo.ContentPanel
31327  * @constructor
31328  * Create a new GridPanel.
31329  * @param {Roo.grid.Grid} grid The grid for this panel
31330  * @param {String/Object} config A string to set only the panel's title, or a config object
31331  */
31332 Roo.GridPanel = function(grid, config){
31333     
31334   
31335     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
31336         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
31337         
31338     this.wrapper.dom.appendChild(grid.getGridEl().dom);
31339     
31340     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
31341     
31342     if(this.toolbar){
31343         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
31344     }
31345     // xtype created footer. - not sure if will work as we normally have to render first..
31346     if (this.footer && !this.footer.el && this.footer.xtype) {
31347         
31348         this.footer.container = this.grid.getView().getFooterPanel(true);
31349         this.footer.dataSource = this.grid.dataSource;
31350         this.footer = Roo.factory(this.footer, Roo);
31351         
31352     }
31353     
31354     grid.monitorWindowResize = false; // turn off autosizing
31355     grid.autoHeight = false;
31356     grid.autoWidth = false;
31357     this.grid = grid;
31358     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
31359 };
31360
31361 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
31362     getId : function(){
31363         return this.grid.id;
31364     },
31365     
31366     /**
31367      * Returns the grid for this panel
31368      * @return {Roo.grid.Grid} 
31369      */
31370     getGrid : function(){
31371         return this.grid;    
31372     },
31373     
31374     setSize : function(width, height){
31375         if(!this.ignoreResize(width, height)){
31376             var grid = this.grid;
31377             var size = this.adjustForComponents(width, height);
31378             grid.getGridEl().setSize(size.width, size.height);
31379             grid.autoSize();
31380         }
31381     },
31382     
31383     beforeSlide : function(){
31384         this.grid.getView().scroller.clip();
31385     },
31386     
31387     afterSlide : function(){
31388         this.grid.getView().scroller.unclip();
31389     },
31390     
31391     destroy : function(){
31392         this.grid.destroy();
31393         delete this.grid;
31394         Roo.GridPanel.superclass.destroy.call(this); 
31395     }
31396 });
31397
31398
31399 /**
31400  * @class Roo.NestedLayoutPanel
31401  * @extends Roo.ContentPanel
31402  * @constructor
31403  * Create a new NestedLayoutPanel.
31404  * 
31405  * 
31406  * @param {Roo.BorderLayout} layout The layout for this panel
31407  * @param {String/Object} config A string to set only the title or a config object
31408  */
31409 Roo.NestedLayoutPanel = function(layout, config)
31410 {
31411     // construct with only one argument..
31412     /* FIXME - implement nicer consturctors
31413     if (layout.layout) {
31414         config = layout;
31415         layout = config.layout;
31416         delete config.layout;
31417     }
31418     if (layout.xtype && !layout.getEl) {
31419         // then layout needs constructing..
31420         layout = Roo.factory(layout, Roo);
31421     }
31422     */
31423     
31424     
31425     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
31426     
31427     layout.monitorWindowResize = false; // turn off autosizing
31428     this.layout = layout;
31429     this.layout.getEl().addClass("x-layout-nested-layout");
31430     
31431     
31432     
31433     
31434 };
31435
31436 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
31437
31438     setSize : function(width, height){
31439         if(!this.ignoreResize(width, height)){
31440             var size = this.adjustForComponents(width, height);
31441             var el = this.layout.getEl();
31442             el.setSize(size.width, size.height);
31443             var touch = el.dom.offsetWidth;
31444             this.layout.layout();
31445             // ie requires a double layout on the first pass
31446             if(Roo.isIE && !this.initialized){
31447                 this.initialized = true;
31448                 this.layout.layout();
31449             }
31450         }
31451     },
31452     
31453     // activate all subpanels if not currently active..
31454     
31455     setActiveState : function(active){
31456         this.active = active;
31457         if(!active){
31458             this.fireEvent("deactivate", this);
31459             return;
31460         }
31461         
31462         this.fireEvent("activate", this);
31463         // not sure if this should happen before or after..
31464         if (!this.layout) {
31465             return; // should not happen..
31466         }
31467         var reg = false;
31468         for (var r in this.layout.regions) {
31469             reg = this.layout.getRegion(r);
31470             if (reg.getActivePanel()) {
31471                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
31472                 reg.setActivePanel(reg.getActivePanel());
31473                 continue;
31474             }
31475             if (!reg.panels.length) {
31476                 continue;
31477             }
31478             reg.showPanel(reg.getPanel(0));
31479         }
31480         
31481         
31482         
31483         
31484     },
31485     
31486     /**
31487      * Returns the nested BorderLayout for this panel
31488      * @return {Roo.BorderLayout} 
31489      */
31490     getLayout : function(){
31491         return this.layout;
31492     },
31493     
31494      /**
31495      * Adds a xtype elements to the layout of the nested panel
31496      * <pre><code>
31497
31498 panel.addxtype({
31499        xtype : 'ContentPanel',
31500        region: 'west',
31501        items: [ .... ]
31502    }
31503 );
31504
31505 panel.addxtype({
31506         xtype : 'NestedLayoutPanel',
31507         region: 'west',
31508         layout: {
31509            center: { },
31510            west: { }   
31511         },
31512         items : [ ... list of content panels or nested layout panels.. ]
31513    }
31514 );
31515 </code></pre>
31516      * @param {Object} cfg Xtype definition of item to add.
31517      */
31518     addxtype : function(cfg) {
31519         return this.layout.addxtype(cfg);
31520     
31521     }
31522 });
31523
31524 Roo.ScrollPanel = function(el, config, content){
31525     config = config || {};
31526     config.fitToFrame = true;
31527     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
31528     
31529     this.el.dom.style.overflow = "hidden";
31530     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
31531     this.el.removeClass("x-layout-inactive-content");
31532     this.el.on("mousewheel", this.onWheel, this);
31533
31534     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
31535     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
31536     up.unselectable(); down.unselectable();
31537     up.on("click", this.scrollUp, this);
31538     down.on("click", this.scrollDown, this);
31539     up.addClassOnOver("x-scroller-btn-over");
31540     down.addClassOnOver("x-scroller-btn-over");
31541     up.addClassOnClick("x-scroller-btn-click");
31542     down.addClassOnClick("x-scroller-btn-click");
31543     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
31544
31545     this.resizeEl = this.el;
31546     this.el = wrap; this.up = up; this.down = down;
31547 };
31548
31549 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
31550     increment : 100,
31551     wheelIncrement : 5,
31552     scrollUp : function(){
31553         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
31554     },
31555
31556     scrollDown : function(){
31557         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
31558     },
31559
31560     afterScroll : function(){
31561         var el = this.resizeEl;
31562         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
31563         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31564         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31565     },
31566
31567     setSize : function(){
31568         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
31569         this.afterScroll();
31570     },
31571
31572     onWheel : function(e){
31573         var d = e.getWheelDelta();
31574         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
31575         this.afterScroll();
31576         e.stopEvent();
31577     },
31578
31579     setContent : function(content, loadScripts){
31580         this.resizeEl.update(content, loadScripts);
31581     }
31582
31583 });
31584
31585
31586
31587
31588
31589
31590
31591
31592
31593 /**
31594  * @class Roo.TreePanel
31595  * @extends Roo.ContentPanel
31596  * @constructor
31597  * Create a new TreePanel. - defaults to fit/scoll contents.
31598  * @param {String/Object} config A string to set only the panel's title, or a config object
31599  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
31600  */
31601 Roo.TreePanel = function(config){
31602     var el = config.el;
31603     var tree = config.tree;
31604     delete config.tree; 
31605     delete config.el; // hopefull!
31606     
31607     // wrapper for IE7 strict & safari scroll issue
31608     
31609     var treeEl = el.createChild();
31610     config.resizeEl = treeEl;
31611     
31612     
31613     
31614     Roo.TreePanel.superclass.constructor.call(this, el, config);
31615  
31616  
31617     this.tree = new Roo.tree.TreePanel(treeEl , tree);
31618     //console.log(tree);
31619     this.on('activate', function()
31620     {
31621         if (this.tree.rendered) {
31622             return;
31623         }
31624         //console.log('render tree');
31625         this.tree.render();
31626     });
31627     // this should not be needed.. - it's actually the 'el' that resizes?
31628     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
31629     
31630     //this.on('resize',  function (cp, w, h) {
31631     //        this.tree.innerCt.setWidth(w);
31632     //        this.tree.innerCt.setHeight(h);
31633     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
31634     //});
31635
31636         
31637     
31638 };
31639
31640 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
31641     fitToFrame : true,
31642     autoScroll : true
31643 });
31644
31645
31646
31647
31648
31649
31650
31651
31652
31653
31654
31655 /*
31656  * Based on:
31657  * Ext JS Library 1.1.1
31658  * Copyright(c) 2006-2007, Ext JS, LLC.
31659  *
31660  * Originally Released Under LGPL - original licence link has changed is not relivant.
31661  *
31662  * Fork - LGPL
31663  * <script type="text/javascript">
31664  */
31665  
31666
31667 /**
31668  * @class Roo.ReaderLayout
31669  * @extends Roo.BorderLayout
31670  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
31671  * center region containing two nested regions (a top one for a list view and one for item preview below),
31672  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
31673  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
31674  * expedites the setup of the overall layout and regions for this common application style.
31675  * Example:
31676  <pre><code>
31677 var reader = new Roo.ReaderLayout();
31678 var CP = Roo.ContentPanel;  // shortcut for adding
31679
31680 reader.beginUpdate();
31681 reader.add("north", new CP("north", "North"));
31682 reader.add("west", new CP("west", {title: "West"}));
31683 reader.add("east", new CP("east", {title: "East"}));
31684
31685 reader.regions.listView.add(new CP("listView", "List"));
31686 reader.regions.preview.add(new CP("preview", "Preview"));
31687 reader.endUpdate();
31688 </code></pre>
31689 * @constructor
31690 * Create a new ReaderLayout
31691 * @param {Object} config Configuration options
31692 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
31693 * document.body if omitted)
31694 */
31695 Roo.ReaderLayout = function(config, renderTo){
31696     var c = config || {size:{}};
31697     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
31698         north: c.north !== false ? Roo.apply({
31699             split:false,
31700             initialSize: 32,
31701             titlebar: false
31702         }, c.north) : false,
31703         west: c.west !== false ? Roo.apply({
31704             split:true,
31705             initialSize: 200,
31706             minSize: 175,
31707             maxSize: 400,
31708             titlebar: true,
31709             collapsible: true,
31710             animate: true,
31711             margins:{left:5,right:0,bottom:5,top:5},
31712             cmargins:{left:5,right:5,bottom:5,top:5}
31713         }, c.west) : false,
31714         east: c.east !== false ? Roo.apply({
31715             split:true,
31716             initialSize: 200,
31717             minSize: 175,
31718             maxSize: 400,
31719             titlebar: true,
31720             collapsible: true,
31721             animate: true,
31722             margins:{left:0,right:5,bottom:5,top:5},
31723             cmargins:{left:5,right:5,bottom:5,top:5}
31724         }, c.east) : false,
31725         center: Roo.apply({
31726             tabPosition: 'top',
31727             autoScroll:false,
31728             closeOnTab: true,
31729             titlebar:false,
31730             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
31731         }, c.center)
31732     });
31733
31734     this.el.addClass('x-reader');
31735
31736     this.beginUpdate();
31737
31738     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
31739         south: c.preview !== false ? Roo.apply({
31740             split:true,
31741             initialSize: 200,
31742             minSize: 100,
31743             autoScroll:true,
31744             collapsible:true,
31745             titlebar: true,
31746             cmargins:{top:5,left:0, right:0, bottom:0}
31747         }, c.preview) : false,
31748         center: Roo.apply({
31749             autoScroll:false,
31750             titlebar:false,
31751             minHeight:200
31752         }, c.listView)
31753     });
31754     this.add('center', new Roo.NestedLayoutPanel(inner,
31755             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
31756
31757     this.endUpdate();
31758
31759     this.regions.preview = inner.getRegion('south');
31760     this.regions.listView = inner.getRegion('center');
31761 };
31762
31763 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
31764  * Based on:
31765  * Ext JS Library 1.1.1
31766  * Copyright(c) 2006-2007, Ext JS, LLC.
31767  *
31768  * Originally Released Under LGPL - original licence link has changed is not relivant.
31769  *
31770  * Fork - LGPL
31771  * <script type="text/javascript">
31772  */
31773  
31774 /**
31775  * @class Roo.grid.Grid
31776  * @extends Roo.util.Observable
31777  * This class represents the primary interface of a component based grid control.
31778  * <br><br>Usage:<pre><code>
31779  var grid = new Roo.grid.Grid("my-container-id", {
31780      ds: myDataStore,
31781      cm: myColModel,
31782      selModel: mySelectionModel,
31783      autoSizeColumns: true,
31784      monitorWindowResize: false,
31785      trackMouseOver: true
31786  });
31787  // set any options
31788  grid.render();
31789  * </code></pre>
31790  * <b>Common Problems:</b><br/>
31791  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
31792  * element will correct this<br/>
31793  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
31794  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
31795  * are unpredictable.<br/>
31796  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
31797  * grid to calculate dimensions/offsets.<br/>
31798   * @constructor
31799  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
31800  * The container MUST have some type of size defined for the grid to fill. The container will be
31801  * automatically set to position relative if it isn't already.
31802  * @param {Object} config A config object that sets properties on this grid.
31803  */
31804 Roo.grid.Grid = function(container, config){
31805         // initialize the container
31806         this.container = Roo.get(container);
31807         this.container.update("");
31808         this.container.setStyle("overflow", "hidden");
31809     this.container.addClass('x-grid-container');
31810
31811     this.id = this.container.id;
31812
31813     Roo.apply(this, config);
31814     // check and correct shorthanded configs
31815     if(this.ds){
31816         this.dataSource = this.ds;
31817         delete this.ds;
31818     }
31819     if(this.cm){
31820         this.colModel = this.cm;
31821         delete this.cm;
31822     }
31823     if(this.sm){
31824         this.selModel = this.sm;
31825         delete this.sm;
31826     }
31827
31828     if (this.selModel) {
31829         this.selModel = Roo.factory(this.selModel, Roo.grid);
31830         this.sm = this.selModel;
31831         this.sm.xmodule = this.xmodule || false;
31832     }
31833     if (typeof(this.colModel.config) == 'undefined') {
31834         this.colModel = new Roo.grid.ColumnModel(this.colModel);
31835         this.cm = this.colModel;
31836         this.cm.xmodule = this.xmodule || false;
31837     }
31838     if (this.dataSource) {
31839         this.dataSource= Roo.factory(this.dataSource, Roo.data);
31840         this.ds = this.dataSource;
31841         this.ds.xmodule = this.xmodule || false;
31842          
31843     }
31844     
31845     
31846     
31847     if(this.width){
31848         this.container.setWidth(this.width);
31849     }
31850
31851     if(this.height){
31852         this.container.setHeight(this.height);
31853     }
31854     /** @private */
31855         this.addEvents({
31856         // raw events
31857         /**
31858          * @event click
31859          * The raw click event for the entire grid.
31860          * @param {Roo.EventObject} e
31861          */
31862         "click" : true,
31863         /**
31864          * @event dblclick
31865          * The raw dblclick event for the entire grid.
31866          * @param {Roo.EventObject} e
31867          */
31868         "dblclick" : true,
31869         /**
31870          * @event contextmenu
31871          * The raw contextmenu event for the entire grid.
31872          * @param {Roo.EventObject} e
31873          */
31874         "contextmenu" : true,
31875         /**
31876          * @event mousedown
31877          * The raw mousedown event for the entire grid.
31878          * @param {Roo.EventObject} e
31879          */
31880         "mousedown" : true,
31881         /**
31882          * @event mouseup
31883          * The raw mouseup event for the entire grid.
31884          * @param {Roo.EventObject} e
31885          */
31886         "mouseup" : true,
31887         /**
31888          * @event mouseover
31889          * The raw mouseover event for the entire grid.
31890          * @param {Roo.EventObject} e
31891          */
31892         "mouseover" : true,
31893         /**
31894          * @event mouseout
31895          * The raw mouseout event for the entire grid.
31896          * @param {Roo.EventObject} e
31897          */
31898         "mouseout" : true,
31899         /**
31900          * @event keypress
31901          * The raw keypress event for the entire grid.
31902          * @param {Roo.EventObject} e
31903          */
31904         "keypress" : true,
31905         /**
31906          * @event keydown
31907          * The raw keydown event for the entire grid.
31908          * @param {Roo.EventObject} e
31909          */
31910         "keydown" : true,
31911
31912         // custom events
31913
31914         /**
31915          * @event cellclick
31916          * Fires when a cell is clicked
31917          * @param {Grid} this
31918          * @param {Number} rowIndex
31919          * @param {Number} columnIndex
31920          * @param {Roo.EventObject} e
31921          */
31922         "cellclick" : true,
31923         /**
31924          * @event celldblclick
31925          * Fires when a cell is double clicked
31926          * @param {Grid} this
31927          * @param {Number} rowIndex
31928          * @param {Number} columnIndex
31929          * @param {Roo.EventObject} e
31930          */
31931         "celldblclick" : true,
31932         /**
31933          * @event rowclick
31934          * Fires when a row is clicked
31935          * @param {Grid} this
31936          * @param {Number} rowIndex
31937          * @param {Roo.EventObject} e
31938          */
31939         "rowclick" : true,
31940         /**
31941          * @event rowdblclick
31942          * Fires when a row is double clicked
31943          * @param {Grid} this
31944          * @param {Number} rowIndex
31945          * @param {Roo.EventObject} e
31946          */
31947         "rowdblclick" : true,
31948         /**
31949          * @event headerclick
31950          * Fires when a header is clicked
31951          * @param {Grid} this
31952          * @param {Number} columnIndex
31953          * @param {Roo.EventObject} e
31954          */
31955         "headerclick" : true,
31956         /**
31957          * @event headerdblclick
31958          * Fires when a header cell is double clicked
31959          * @param {Grid} this
31960          * @param {Number} columnIndex
31961          * @param {Roo.EventObject} e
31962          */
31963         "headerdblclick" : true,
31964         /**
31965          * @event rowcontextmenu
31966          * Fires when a row is right clicked
31967          * @param {Grid} this
31968          * @param {Number} rowIndex
31969          * @param {Roo.EventObject} e
31970          */
31971         "rowcontextmenu" : true,
31972         /**
31973          * @event cellcontextmenu
31974          * Fires when a cell is right clicked
31975          * @param {Grid} this
31976          * @param {Number} rowIndex
31977          * @param {Number} cellIndex
31978          * @param {Roo.EventObject} e
31979          */
31980          "cellcontextmenu" : true,
31981         /**
31982          * @event headercontextmenu
31983          * Fires when a header is right clicked
31984          * @param {Grid} this
31985          * @param {Number} columnIndex
31986          * @param {Roo.EventObject} e
31987          */
31988         "headercontextmenu" : true,
31989         /**
31990          * @event bodyscroll
31991          * Fires when the body element is scrolled
31992          * @param {Number} scrollLeft
31993          * @param {Number} scrollTop
31994          */
31995         "bodyscroll" : true,
31996         /**
31997          * @event columnresize
31998          * Fires when the user resizes a column
31999          * @param {Number} columnIndex
32000          * @param {Number} newSize
32001          */
32002         "columnresize" : true,
32003         /**
32004          * @event columnmove
32005          * Fires when the user moves a column
32006          * @param {Number} oldIndex
32007          * @param {Number} newIndex
32008          */
32009         "columnmove" : true,
32010         /**
32011          * @event startdrag
32012          * Fires when row(s) start being dragged
32013          * @param {Grid} this
32014          * @param {Roo.GridDD} dd The drag drop object
32015          * @param {event} e The raw browser event
32016          */
32017         "startdrag" : true,
32018         /**
32019          * @event enddrag
32020          * Fires when a drag operation is complete
32021          * @param {Grid} this
32022          * @param {Roo.GridDD} dd The drag drop object
32023          * @param {event} e The raw browser event
32024          */
32025         "enddrag" : true,
32026         /**
32027          * @event dragdrop
32028          * Fires when dragged row(s) are dropped on a valid DD target
32029          * @param {Grid} this
32030          * @param {Roo.GridDD} dd The drag drop object
32031          * @param {String} targetId The target drag drop object
32032          * @param {event} e The raw browser event
32033          */
32034         "dragdrop" : true,
32035         /**
32036          * @event dragover
32037          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
32038          * @param {Grid} this
32039          * @param {Roo.GridDD} dd The drag drop object
32040          * @param {String} targetId The target drag drop object
32041          * @param {event} e The raw browser event
32042          */
32043         "dragover" : true,
32044         /**
32045          * @event dragenter
32046          *  Fires when the dragged row(s) first cross another DD target while being dragged
32047          * @param {Grid} this
32048          * @param {Roo.GridDD} dd The drag drop object
32049          * @param {String} targetId The target drag drop object
32050          * @param {event} e The raw browser event
32051          */
32052         "dragenter" : true,
32053         /**
32054          * @event dragout
32055          * Fires when the dragged row(s) leave another DD target while being dragged
32056          * @param {Grid} this
32057          * @param {Roo.GridDD} dd The drag drop object
32058          * @param {String} targetId The target drag drop object
32059          * @param {event} e The raw browser event
32060          */
32061         "dragout" : true,
32062         /**
32063          * @event rowclass
32064          * Fires when a row is rendered, so you can change add a style to it.
32065          * @param {GridView} gridview   The grid view
32066          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
32067          */
32068         'rowclass' : true,
32069
32070         /**
32071          * @event render
32072          * Fires when the grid is rendered
32073          * @param {Grid} grid
32074          */
32075         'render' : true
32076     });
32077
32078     Roo.grid.Grid.superclass.constructor.call(this);
32079 };
32080 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
32081     
32082     /**
32083      * @cfg {String} ddGroup - drag drop group.
32084      */
32085
32086     /**
32087      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
32088      */
32089     minColumnWidth : 25,
32090
32091     /**
32092      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
32093      * <b>on initial render.</b> It is more efficient to explicitly size the columns
32094      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
32095      */
32096     autoSizeColumns : false,
32097
32098     /**
32099      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
32100      */
32101     autoSizeHeaders : true,
32102
32103     /**
32104      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
32105      */
32106     monitorWindowResize : true,
32107
32108     /**
32109      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
32110      * rows measured to get a columns size. Default is 0 (all rows).
32111      */
32112     maxRowsToMeasure : 0,
32113
32114     /**
32115      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
32116      */
32117     trackMouseOver : true,
32118
32119     /**
32120     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
32121     */
32122     
32123     /**
32124     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
32125     */
32126     enableDragDrop : false,
32127     
32128     /**
32129     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
32130     */
32131     enableColumnMove : true,
32132     
32133     /**
32134     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
32135     */
32136     enableColumnHide : true,
32137     
32138     /**
32139     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
32140     */
32141     enableRowHeightSync : false,
32142     
32143     /**
32144     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
32145     */
32146     stripeRows : true,
32147     
32148     /**
32149     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
32150     */
32151     autoHeight : false,
32152
32153     /**
32154      * @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.
32155      */
32156     autoExpandColumn : false,
32157
32158     /**
32159     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
32160     * Default is 50.
32161     */
32162     autoExpandMin : 50,
32163
32164     /**
32165     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
32166     */
32167     autoExpandMax : 1000,
32168
32169     /**
32170     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
32171     */
32172     view : null,
32173
32174     /**
32175     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
32176     */
32177     loadMask : false,
32178     /**
32179     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
32180     */
32181     dropTarget: false,
32182     
32183    
32184     
32185     // private
32186     rendered : false,
32187
32188     /**
32189     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
32190     * of a fixed width. Default is false.
32191     */
32192     /**
32193     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
32194     */
32195     /**
32196      * Called once after all setup has been completed and the grid is ready to be rendered.
32197      * @return {Roo.grid.Grid} this
32198      */
32199     render : function()
32200     {
32201         var c = this.container;
32202         // try to detect autoHeight/width mode
32203         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
32204             this.autoHeight = true;
32205         }
32206         var view = this.getView();
32207         view.init(this);
32208
32209         c.on("click", this.onClick, this);
32210         c.on("dblclick", this.onDblClick, this);
32211         c.on("contextmenu", this.onContextMenu, this);
32212         c.on("keydown", this.onKeyDown, this);
32213         if (Roo.isTouch) {
32214             c.on("touchstart", this.onTouchStart, this);
32215         }
32216
32217         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
32218
32219         this.getSelectionModel().init(this);
32220
32221         view.render();
32222
32223         if(this.loadMask){
32224             this.loadMask = new Roo.LoadMask(this.container,
32225                     Roo.apply({store:this.dataSource}, this.loadMask));
32226         }
32227         
32228         
32229         if (this.toolbar && this.toolbar.xtype) {
32230             this.toolbar.container = this.getView().getHeaderPanel(true);
32231             this.toolbar = new Roo.Toolbar(this.toolbar);
32232         }
32233         if (this.footer && this.footer.xtype) {
32234             this.footer.dataSource = this.getDataSource();
32235             this.footer.container = this.getView().getFooterPanel(true);
32236             this.footer = Roo.factory(this.footer, Roo);
32237         }
32238         if (this.dropTarget && this.dropTarget.xtype) {
32239             delete this.dropTarget.xtype;
32240             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
32241         }
32242         
32243         
32244         this.rendered = true;
32245         this.fireEvent('render', this);
32246         return this;
32247     },
32248
32249         /**
32250          * Reconfigures the grid to use a different Store and Column Model.
32251          * The View will be bound to the new objects and refreshed.
32252          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
32253          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
32254          */
32255     reconfigure : function(dataSource, colModel){
32256         if(this.loadMask){
32257             this.loadMask.destroy();
32258             this.loadMask = new Roo.LoadMask(this.container,
32259                     Roo.apply({store:dataSource}, this.loadMask));
32260         }
32261         this.view.bind(dataSource, colModel);
32262         this.dataSource = dataSource;
32263         this.colModel = colModel;
32264         this.view.refresh(true);
32265     },
32266
32267     // private
32268     onKeyDown : function(e){
32269         this.fireEvent("keydown", e);
32270     },
32271
32272     /**
32273      * Destroy this grid.
32274      * @param {Boolean} removeEl True to remove the element
32275      */
32276     destroy : function(removeEl, keepListeners){
32277         if(this.loadMask){
32278             this.loadMask.destroy();
32279         }
32280         var c = this.container;
32281         c.removeAllListeners();
32282         this.view.destroy();
32283         this.colModel.purgeListeners();
32284         if(!keepListeners){
32285             this.purgeListeners();
32286         }
32287         c.update("");
32288         if(removeEl === true){
32289             c.remove();
32290         }
32291     },
32292
32293     // private
32294     processEvent : function(name, e){
32295         // does this fire select???
32296         //Roo.log('grid:processEvent '  + name);
32297         
32298         if (name != 'touchstart' ) {
32299             this.fireEvent(name, e);    
32300         }
32301         
32302         var t = e.getTarget();
32303         var v = this.view;
32304         var header = v.findHeaderIndex(t);
32305         if(header !== false){
32306             var ename = name == 'touchstart' ? 'click' : name;
32307              
32308             this.fireEvent("header" + ename, this, header, e);
32309         }else{
32310             var row = v.findRowIndex(t);
32311             var cell = v.findCellIndex(t);
32312             if (name == 'touchstart') {
32313                 // first touch is always a click.
32314                 // hopefull this happens after selection is updated.?
32315                 name = false;
32316                 
32317                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
32318                     var cs = this.selModel.getSelectedCell();
32319                     if (row == cs[0] && cell == cs[1]){
32320                         name = 'dblclick';
32321                     }
32322                 }
32323                 if (typeof(this.selModel.getSelections) != 'undefined') {
32324                     var cs = this.selModel.getSelections();
32325                     var ds = this.dataSource;
32326                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
32327                         name = 'dblclick';
32328                     }
32329                 }
32330                 if (!name) {
32331                     return;
32332                 }
32333             }
32334             
32335             
32336             if(row !== false){
32337                 this.fireEvent("row" + name, this, row, e);
32338                 if(cell !== false){
32339                     this.fireEvent("cell" + name, this, row, cell, e);
32340                 }
32341             }
32342         }
32343     },
32344
32345     // private
32346     onClick : function(e){
32347         this.processEvent("click", e);
32348     },
32349    // private
32350     onTouchStart : function(e){
32351         this.processEvent("touchstart", e);
32352     },
32353
32354     // private
32355     onContextMenu : function(e, t){
32356         this.processEvent("contextmenu", e);
32357     },
32358
32359     // private
32360     onDblClick : function(e){
32361         this.processEvent("dblclick", e);
32362     },
32363
32364     // private
32365     walkCells : function(row, col, step, fn, scope){
32366         var cm = this.colModel, clen = cm.getColumnCount();
32367         var ds = this.dataSource, rlen = ds.getCount(), first = true;
32368         if(step < 0){
32369             if(col < 0){
32370                 row--;
32371                 first = false;
32372             }
32373             while(row >= 0){
32374                 if(!first){
32375                     col = clen-1;
32376                 }
32377                 first = false;
32378                 while(col >= 0){
32379                     if(fn.call(scope || this, row, col, cm) === true){
32380                         return [row, col];
32381                     }
32382                     col--;
32383                 }
32384                 row--;
32385             }
32386         } else {
32387             if(col >= clen){
32388                 row++;
32389                 first = false;
32390             }
32391             while(row < rlen){
32392                 if(!first){
32393                     col = 0;
32394                 }
32395                 first = false;
32396                 while(col < clen){
32397                     if(fn.call(scope || this, row, col, cm) === true){
32398                         return [row, col];
32399                     }
32400                     col++;
32401                 }
32402                 row++;
32403             }
32404         }
32405         return null;
32406     },
32407
32408     // private
32409     getSelections : function(){
32410         return this.selModel.getSelections();
32411     },
32412
32413     /**
32414      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
32415      * but if manual update is required this method will initiate it.
32416      */
32417     autoSize : function(){
32418         if(this.rendered){
32419             this.view.layout();
32420             if(this.view.adjustForScroll){
32421                 this.view.adjustForScroll();
32422             }
32423         }
32424     },
32425
32426     /**
32427      * Returns the grid's underlying element.
32428      * @return {Element} The element
32429      */
32430     getGridEl : function(){
32431         return this.container;
32432     },
32433
32434     // private for compatibility, overridden by editor grid
32435     stopEditing : function(){},
32436
32437     /**
32438      * Returns the grid's SelectionModel.
32439      * @return {SelectionModel}
32440      */
32441     getSelectionModel : function(){
32442         if(!this.selModel){
32443             this.selModel = new Roo.grid.RowSelectionModel();
32444         }
32445         return this.selModel;
32446     },
32447
32448     /**
32449      * Returns the grid's DataSource.
32450      * @return {DataSource}
32451      */
32452     getDataSource : function(){
32453         return this.dataSource;
32454     },
32455
32456     /**
32457      * Returns the grid's ColumnModel.
32458      * @return {ColumnModel}
32459      */
32460     getColumnModel : function(){
32461         return this.colModel;
32462     },
32463
32464     /**
32465      * Returns the grid's GridView object.
32466      * @return {GridView}
32467      */
32468     getView : function(){
32469         if(!this.view){
32470             this.view = new Roo.grid.GridView(this.viewConfig);
32471         }
32472         return this.view;
32473     },
32474     /**
32475      * Called to get grid's drag proxy text, by default returns this.ddText.
32476      * @return {String}
32477      */
32478     getDragDropText : function(){
32479         var count = this.selModel.getCount();
32480         return String.format(this.ddText, count, count == 1 ? '' : 's');
32481     }
32482 });
32483 /**
32484  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
32485  * %0 is replaced with the number of selected rows.
32486  * @type String
32487  */
32488 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
32489  * Based on:
32490  * Ext JS Library 1.1.1
32491  * Copyright(c) 2006-2007, Ext JS, LLC.
32492  *
32493  * Originally Released Under LGPL - original licence link has changed is not relivant.
32494  *
32495  * Fork - LGPL
32496  * <script type="text/javascript">
32497  */
32498  
32499 Roo.grid.AbstractGridView = function(){
32500         this.grid = null;
32501         
32502         this.events = {
32503             "beforerowremoved" : true,
32504             "beforerowsinserted" : true,
32505             "beforerefresh" : true,
32506             "rowremoved" : true,
32507             "rowsinserted" : true,
32508             "rowupdated" : true,
32509             "refresh" : true
32510         };
32511     Roo.grid.AbstractGridView.superclass.constructor.call(this);
32512 };
32513
32514 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
32515     rowClass : "x-grid-row",
32516     cellClass : "x-grid-cell",
32517     tdClass : "x-grid-td",
32518     hdClass : "x-grid-hd",
32519     splitClass : "x-grid-hd-split",
32520     
32521     init: function(grid){
32522         this.grid = grid;
32523                 var cid = this.grid.getGridEl().id;
32524         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
32525         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
32526         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
32527         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
32528         },
32529         
32530     getColumnRenderers : function(){
32531         var renderers = [];
32532         var cm = this.grid.colModel;
32533         var colCount = cm.getColumnCount();
32534         for(var i = 0; i < colCount; i++){
32535             renderers[i] = cm.getRenderer(i);
32536         }
32537         return renderers;
32538     },
32539     
32540     getColumnIds : function(){
32541         var ids = [];
32542         var cm = this.grid.colModel;
32543         var colCount = cm.getColumnCount();
32544         for(var i = 0; i < colCount; i++){
32545             ids[i] = cm.getColumnId(i);
32546         }
32547         return ids;
32548     },
32549     
32550     getDataIndexes : function(){
32551         if(!this.indexMap){
32552             this.indexMap = this.buildIndexMap();
32553         }
32554         return this.indexMap.colToData;
32555     },
32556     
32557     getColumnIndexByDataIndex : function(dataIndex){
32558         if(!this.indexMap){
32559             this.indexMap = this.buildIndexMap();
32560         }
32561         return this.indexMap.dataToCol[dataIndex];
32562     },
32563     
32564     /**
32565      * Set a css style for a column dynamically. 
32566      * @param {Number} colIndex The index of the column
32567      * @param {String} name The css property name
32568      * @param {String} value The css value
32569      */
32570     setCSSStyle : function(colIndex, name, value){
32571         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
32572         Roo.util.CSS.updateRule(selector, name, value);
32573     },
32574     
32575     generateRules : function(cm){
32576         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
32577         Roo.util.CSS.removeStyleSheet(rulesId);
32578         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
32579             var cid = cm.getColumnId(i);
32580             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
32581                          this.tdSelector, cid, " {\n}\n",
32582                          this.hdSelector, cid, " {\n}\n",
32583                          this.splitSelector, cid, " {\n}\n");
32584         }
32585         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
32586     }
32587 });/*
32588  * Based on:
32589  * Ext JS Library 1.1.1
32590  * Copyright(c) 2006-2007, Ext JS, LLC.
32591  *
32592  * Originally Released Under LGPL - original licence link has changed is not relivant.
32593  *
32594  * Fork - LGPL
32595  * <script type="text/javascript">
32596  */
32597
32598 // private
32599 // This is a support class used internally by the Grid components
32600 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
32601     this.grid = grid;
32602     this.view = grid.getView();
32603     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
32604     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
32605     if(hd2){
32606         this.setHandleElId(Roo.id(hd));
32607         this.setOuterHandleElId(Roo.id(hd2));
32608     }
32609     this.scroll = false;
32610 };
32611 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
32612     maxDragWidth: 120,
32613     getDragData : function(e){
32614         var t = Roo.lib.Event.getTarget(e);
32615         var h = this.view.findHeaderCell(t);
32616         if(h){
32617             return {ddel: h.firstChild, header:h};
32618         }
32619         return false;
32620     },
32621
32622     onInitDrag : function(e){
32623         this.view.headersDisabled = true;
32624         var clone = this.dragData.ddel.cloneNode(true);
32625         clone.id = Roo.id();
32626         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
32627         this.proxy.update(clone);
32628         return true;
32629     },
32630
32631     afterValidDrop : function(){
32632         var v = this.view;
32633         setTimeout(function(){
32634             v.headersDisabled = false;
32635         }, 50);
32636     },
32637
32638     afterInvalidDrop : function(){
32639         var v = this.view;
32640         setTimeout(function(){
32641             v.headersDisabled = false;
32642         }, 50);
32643     }
32644 });
32645 /*
32646  * Based on:
32647  * Ext JS Library 1.1.1
32648  * Copyright(c) 2006-2007, Ext JS, LLC.
32649  *
32650  * Originally Released Under LGPL - original licence link has changed is not relivant.
32651  *
32652  * Fork - LGPL
32653  * <script type="text/javascript">
32654  */
32655 // private
32656 // This is a support class used internally by the Grid components
32657 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
32658     this.grid = grid;
32659     this.view = grid.getView();
32660     // split the proxies so they don't interfere with mouse events
32661     this.proxyTop = Roo.DomHelper.append(document.body, {
32662         cls:"col-move-top", html:"&#160;"
32663     }, true);
32664     this.proxyBottom = Roo.DomHelper.append(document.body, {
32665         cls:"col-move-bottom", html:"&#160;"
32666     }, true);
32667     this.proxyTop.hide = this.proxyBottom.hide = function(){
32668         this.setLeftTop(-100,-100);
32669         this.setStyle("visibility", "hidden");
32670     };
32671     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
32672     // temporarily disabled
32673     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
32674     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
32675 };
32676 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
32677     proxyOffsets : [-4, -9],
32678     fly: Roo.Element.fly,
32679
32680     getTargetFromEvent : function(e){
32681         var t = Roo.lib.Event.getTarget(e);
32682         var cindex = this.view.findCellIndex(t);
32683         if(cindex !== false){
32684             return this.view.getHeaderCell(cindex);
32685         }
32686         return null;
32687     },
32688
32689     nextVisible : function(h){
32690         var v = this.view, cm = this.grid.colModel;
32691         h = h.nextSibling;
32692         while(h){
32693             if(!cm.isHidden(v.getCellIndex(h))){
32694                 return h;
32695             }
32696             h = h.nextSibling;
32697         }
32698         return null;
32699     },
32700
32701     prevVisible : function(h){
32702         var v = this.view, cm = this.grid.colModel;
32703         h = h.prevSibling;
32704         while(h){
32705             if(!cm.isHidden(v.getCellIndex(h))){
32706                 return h;
32707             }
32708             h = h.prevSibling;
32709         }
32710         return null;
32711     },
32712
32713     positionIndicator : function(h, n, e){
32714         var x = Roo.lib.Event.getPageX(e);
32715         var r = Roo.lib.Dom.getRegion(n.firstChild);
32716         var px, pt, py = r.top + this.proxyOffsets[1];
32717         if((r.right - x) <= (r.right-r.left)/2){
32718             px = r.right+this.view.borderWidth;
32719             pt = "after";
32720         }else{
32721             px = r.left;
32722             pt = "before";
32723         }
32724         var oldIndex = this.view.getCellIndex(h);
32725         var newIndex = this.view.getCellIndex(n);
32726
32727         if(this.grid.colModel.isFixed(newIndex)){
32728             return false;
32729         }
32730
32731         var locked = this.grid.colModel.isLocked(newIndex);
32732
32733         if(pt == "after"){
32734             newIndex++;
32735         }
32736         if(oldIndex < newIndex){
32737             newIndex--;
32738         }
32739         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
32740             return false;
32741         }
32742         px +=  this.proxyOffsets[0];
32743         this.proxyTop.setLeftTop(px, py);
32744         this.proxyTop.show();
32745         if(!this.bottomOffset){
32746             this.bottomOffset = this.view.mainHd.getHeight();
32747         }
32748         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
32749         this.proxyBottom.show();
32750         return pt;
32751     },
32752
32753     onNodeEnter : function(n, dd, e, data){
32754         if(data.header != n){
32755             this.positionIndicator(data.header, n, e);
32756         }
32757     },
32758
32759     onNodeOver : function(n, dd, e, data){
32760         var result = false;
32761         if(data.header != n){
32762             result = this.positionIndicator(data.header, n, e);
32763         }
32764         if(!result){
32765             this.proxyTop.hide();
32766             this.proxyBottom.hide();
32767         }
32768         return result ? this.dropAllowed : this.dropNotAllowed;
32769     },
32770
32771     onNodeOut : function(n, dd, e, data){
32772         this.proxyTop.hide();
32773         this.proxyBottom.hide();
32774     },
32775
32776     onNodeDrop : function(n, dd, e, data){
32777         var h = data.header;
32778         if(h != n){
32779             var cm = this.grid.colModel;
32780             var x = Roo.lib.Event.getPageX(e);
32781             var r = Roo.lib.Dom.getRegion(n.firstChild);
32782             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
32783             var oldIndex = this.view.getCellIndex(h);
32784             var newIndex = this.view.getCellIndex(n);
32785             var locked = cm.isLocked(newIndex);
32786             if(pt == "after"){
32787                 newIndex++;
32788             }
32789             if(oldIndex < newIndex){
32790                 newIndex--;
32791             }
32792             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
32793                 return false;
32794             }
32795             cm.setLocked(oldIndex, locked, true);
32796             cm.moveColumn(oldIndex, newIndex);
32797             this.grid.fireEvent("columnmove", oldIndex, newIndex);
32798             return true;
32799         }
32800         return false;
32801     }
32802 });
32803 /*
32804  * Based on:
32805  * Ext JS Library 1.1.1
32806  * Copyright(c) 2006-2007, Ext JS, LLC.
32807  *
32808  * Originally Released Under LGPL - original licence link has changed is not relivant.
32809  *
32810  * Fork - LGPL
32811  * <script type="text/javascript">
32812  */
32813   
32814 /**
32815  * @class Roo.grid.GridView
32816  * @extends Roo.util.Observable
32817  *
32818  * @constructor
32819  * @param {Object} config
32820  */
32821 Roo.grid.GridView = function(config){
32822     Roo.grid.GridView.superclass.constructor.call(this);
32823     this.el = null;
32824
32825     Roo.apply(this, config);
32826 };
32827
32828 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
32829
32830     unselectable :  'unselectable="on"',
32831     unselectableCls :  'x-unselectable',
32832     
32833     
32834     rowClass : "x-grid-row",
32835
32836     cellClass : "x-grid-col",
32837
32838     tdClass : "x-grid-td",
32839
32840     hdClass : "x-grid-hd",
32841
32842     splitClass : "x-grid-split",
32843
32844     sortClasses : ["sort-asc", "sort-desc"],
32845
32846     enableMoveAnim : false,
32847
32848     hlColor: "C3DAF9",
32849
32850     dh : Roo.DomHelper,
32851
32852     fly : Roo.Element.fly,
32853
32854     css : Roo.util.CSS,
32855
32856     borderWidth: 1,
32857
32858     splitOffset: 3,
32859
32860     scrollIncrement : 22,
32861
32862     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
32863
32864     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
32865
32866     bind : function(ds, cm){
32867         if(this.ds){
32868             this.ds.un("load", this.onLoad, this);
32869             this.ds.un("datachanged", this.onDataChange, this);
32870             this.ds.un("add", this.onAdd, this);
32871             this.ds.un("remove", this.onRemove, this);
32872             this.ds.un("update", this.onUpdate, this);
32873             this.ds.un("clear", this.onClear, this);
32874         }
32875         if(ds){
32876             ds.on("load", this.onLoad, this);
32877             ds.on("datachanged", this.onDataChange, this);
32878             ds.on("add", this.onAdd, this);
32879             ds.on("remove", this.onRemove, this);
32880             ds.on("update", this.onUpdate, this);
32881             ds.on("clear", this.onClear, this);
32882         }
32883         this.ds = ds;
32884
32885         if(this.cm){
32886             this.cm.un("widthchange", this.onColWidthChange, this);
32887             this.cm.un("headerchange", this.onHeaderChange, this);
32888             this.cm.un("hiddenchange", this.onHiddenChange, this);
32889             this.cm.un("columnmoved", this.onColumnMove, this);
32890             this.cm.un("columnlockchange", this.onColumnLock, this);
32891         }
32892         if(cm){
32893             this.generateRules(cm);
32894             cm.on("widthchange", this.onColWidthChange, this);
32895             cm.on("headerchange", this.onHeaderChange, this);
32896             cm.on("hiddenchange", this.onHiddenChange, this);
32897             cm.on("columnmoved", this.onColumnMove, this);
32898             cm.on("columnlockchange", this.onColumnLock, this);
32899         }
32900         this.cm = cm;
32901     },
32902
32903     init: function(grid){
32904         Roo.grid.GridView.superclass.init.call(this, grid);
32905
32906         this.bind(grid.dataSource, grid.colModel);
32907
32908         grid.on("headerclick", this.handleHeaderClick, this);
32909
32910         if(grid.trackMouseOver){
32911             grid.on("mouseover", this.onRowOver, this);
32912             grid.on("mouseout", this.onRowOut, this);
32913         }
32914         grid.cancelTextSelection = function(){};
32915         this.gridId = grid.id;
32916
32917         var tpls = this.templates || {};
32918
32919         if(!tpls.master){
32920             tpls.master = new Roo.Template(
32921                '<div class="x-grid" hidefocus="true">',
32922                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
32923                   '<div class="x-grid-topbar"></div>',
32924                   '<div class="x-grid-scroller"><div></div></div>',
32925                   '<div class="x-grid-locked">',
32926                       '<div class="x-grid-header">{lockedHeader}</div>',
32927                       '<div class="x-grid-body">{lockedBody}</div>',
32928                   "</div>",
32929                   '<div class="x-grid-viewport">',
32930                       '<div class="x-grid-header">{header}</div>',
32931                       '<div class="x-grid-body">{body}</div>',
32932                   "</div>",
32933                   '<div class="x-grid-bottombar"></div>',
32934                  
32935                   '<div class="x-grid-resize-proxy">&#160;</div>',
32936                "</div>"
32937             );
32938             tpls.master.disableformats = true;
32939         }
32940
32941         if(!tpls.header){
32942             tpls.header = new Roo.Template(
32943                '<table border="0" cellspacing="0" cellpadding="0">',
32944                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
32945                "</table>{splits}"
32946             );
32947             tpls.header.disableformats = true;
32948         }
32949         tpls.header.compile();
32950
32951         if(!tpls.hcell){
32952             tpls.hcell = new Roo.Template(
32953                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
32954                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
32955                 "</div></td>"
32956              );
32957              tpls.hcell.disableFormats = true;
32958         }
32959         tpls.hcell.compile();
32960
32961         if(!tpls.hsplit){
32962             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
32963                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
32964             tpls.hsplit.disableFormats = true;
32965         }
32966         tpls.hsplit.compile();
32967
32968         if(!tpls.body){
32969             tpls.body = new Roo.Template(
32970                '<table border="0" cellspacing="0" cellpadding="0">',
32971                "<tbody>{rows}</tbody>",
32972                "</table>"
32973             );
32974             tpls.body.disableFormats = true;
32975         }
32976         tpls.body.compile();
32977
32978         if(!tpls.row){
32979             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
32980             tpls.row.disableFormats = true;
32981         }
32982         tpls.row.compile();
32983
32984         if(!tpls.cell){
32985             tpls.cell = new Roo.Template(
32986                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
32987                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
32988                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
32989                 "</td>"
32990             );
32991             tpls.cell.disableFormats = true;
32992         }
32993         tpls.cell.compile();
32994
32995         this.templates = tpls;
32996     },
32997
32998     // remap these for backwards compat
32999     onColWidthChange : function(){
33000         this.updateColumns.apply(this, arguments);
33001     },
33002     onHeaderChange : function(){
33003         this.updateHeaders.apply(this, arguments);
33004     }, 
33005     onHiddenChange : function(){
33006         this.handleHiddenChange.apply(this, arguments);
33007     },
33008     onColumnMove : function(){
33009         this.handleColumnMove.apply(this, arguments);
33010     },
33011     onColumnLock : function(){
33012         this.handleLockChange.apply(this, arguments);
33013     },
33014
33015     onDataChange : function(){
33016         this.refresh();
33017         this.updateHeaderSortState();
33018     },
33019
33020     onClear : function(){
33021         this.refresh();
33022     },
33023
33024     onUpdate : function(ds, record){
33025         this.refreshRow(record);
33026     },
33027
33028     refreshRow : function(record){
33029         var ds = this.ds, index;
33030         if(typeof record == 'number'){
33031             index = record;
33032             record = ds.getAt(index);
33033         }else{
33034             index = ds.indexOf(record);
33035         }
33036         this.insertRows(ds, index, index, true);
33037         this.onRemove(ds, record, index+1, true);
33038         this.syncRowHeights(index, index);
33039         this.layout();
33040         this.fireEvent("rowupdated", this, index, record);
33041     },
33042
33043     onAdd : function(ds, records, index){
33044         this.insertRows(ds, index, index + (records.length-1));
33045     },
33046
33047     onRemove : function(ds, record, index, isUpdate){
33048         if(isUpdate !== true){
33049             this.fireEvent("beforerowremoved", this, index, record);
33050         }
33051         var bt = this.getBodyTable(), lt = this.getLockedTable();
33052         if(bt.rows[index]){
33053             bt.firstChild.removeChild(bt.rows[index]);
33054         }
33055         if(lt.rows[index]){
33056             lt.firstChild.removeChild(lt.rows[index]);
33057         }
33058         if(isUpdate !== true){
33059             this.stripeRows(index);
33060             this.syncRowHeights(index, index);
33061             this.layout();
33062             this.fireEvent("rowremoved", this, index, record);
33063         }
33064     },
33065
33066     onLoad : function(){
33067         this.scrollToTop();
33068     },
33069
33070     /**
33071      * Scrolls the grid to the top
33072      */
33073     scrollToTop : function(){
33074         if(this.scroller){
33075             this.scroller.dom.scrollTop = 0;
33076             this.syncScroll();
33077         }
33078     },
33079
33080     /**
33081      * Gets a panel in the header of the grid that can be used for toolbars etc.
33082      * After modifying the contents of this panel a call to grid.autoSize() may be
33083      * required to register any changes in size.
33084      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
33085      * @return Roo.Element
33086      */
33087     getHeaderPanel : function(doShow){
33088         if(doShow){
33089             this.headerPanel.show();
33090         }
33091         return this.headerPanel;
33092     },
33093
33094     /**
33095      * Gets a panel in the footer of the grid that can be used for toolbars etc.
33096      * After modifying the contents of this panel a call to grid.autoSize() may be
33097      * required to register any changes in size.
33098      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
33099      * @return Roo.Element
33100      */
33101     getFooterPanel : function(doShow){
33102         if(doShow){
33103             this.footerPanel.show();
33104         }
33105         return this.footerPanel;
33106     },
33107
33108     initElements : function(){
33109         var E = Roo.Element;
33110         var el = this.grid.getGridEl().dom.firstChild;
33111         var cs = el.childNodes;
33112
33113         this.el = new E(el);
33114         
33115          this.focusEl = new E(el.firstChild);
33116         this.focusEl.swallowEvent("click", true);
33117         
33118         this.headerPanel = new E(cs[1]);
33119         this.headerPanel.enableDisplayMode("block");
33120
33121         this.scroller = new E(cs[2]);
33122         this.scrollSizer = new E(this.scroller.dom.firstChild);
33123
33124         this.lockedWrap = new E(cs[3]);
33125         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
33126         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
33127
33128         this.mainWrap = new E(cs[4]);
33129         this.mainHd = new E(this.mainWrap.dom.firstChild);
33130         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
33131
33132         this.footerPanel = new E(cs[5]);
33133         this.footerPanel.enableDisplayMode("block");
33134
33135         this.resizeProxy = new E(cs[6]);
33136
33137         this.headerSelector = String.format(
33138            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
33139            this.lockedHd.id, this.mainHd.id
33140         );
33141
33142         this.splitterSelector = String.format(
33143            '#{0} div.x-grid-split, #{1} div.x-grid-split',
33144            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
33145         );
33146     },
33147     idToCssName : function(s)
33148     {
33149         return s.replace(/[^a-z0-9]+/ig, '-');
33150     },
33151
33152     getHeaderCell : function(index){
33153         return Roo.DomQuery.select(this.headerSelector)[index];
33154     },
33155
33156     getHeaderCellMeasure : function(index){
33157         return this.getHeaderCell(index).firstChild;
33158     },
33159
33160     getHeaderCellText : function(index){
33161         return this.getHeaderCell(index).firstChild.firstChild;
33162     },
33163
33164     getLockedTable : function(){
33165         return this.lockedBody.dom.firstChild;
33166     },
33167
33168     getBodyTable : function(){
33169         return this.mainBody.dom.firstChild;
33170     },
33171
33172     getLockedRow : function(index){
33173         return this.getLockedTable().rows[index];
33174     },
33175
33176     getRow : function(index){
33177         return this.getBodyTable().rows[index];
33178     },
33179
33180     getRowComposite : function(index){
33181         if(!this.rowEl){
33182             this.rowEl = new Roo.CompositeElementLite();
33183         }
33184         var els = [], lrow, mrow;
33185         if(lrow = this.getLockedRow(index)){
33186             els.push(lrow);
33187         }
33188         if(mrow = this.getRow(index)){
33189             els.push(mrow);
33190         }
33191         this.rowEl.elements = els;
33192         return this.rowEl;
33193     },
33194     /**
33195      * Gets the 'td' of the cell
33196      * 
33197      * @param {Integer} rowIndex row to select
33198      * @param {Integer} colIndex column to select
33199      * 
33200      * @return {Object} 
33201      */
33202     getCell : function(rowIndex, colIndex){
33203         var locked = this.cm.getLockedCount();
33204         var source;
33205         if(colIndex < locked){
33206             source = this.lockedBody.dom.firstChild;
33207         }else{
33208             source = this.mainBody.dom.firstChild;
33209             colIndex -= locked;
33210         }
33211         return source.rows[rowIndex].childNodes[colIndex];
33212     },
33213
33214     getCellText : function(rowIndex, colIndex){
33215         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
33216     },
33217
33218     getCellBox : function(cell){
33219         var b = this.fly(cell).getBox();
33220         if(Roo.isOpera){ // opera fails to report the Y
33221             b.y = cell.offsetTop + this.mainBody.getY();
33222         }
33223         return b;
33224     },
33225
33226     getCellIndex : function(cell){
33227         var id = String(cell.className).match(this.cellRE);
33228         if(id){
33229             return parseInt(id[1], 10);
33230         }
33231         return 0;
33232     },
33233
33234     findHeaderIndex : function(n){
33235         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33236         return r ? this.getCellIndex(r) : false;
33237     },
33238
33239     findHeaderCell : function(n){
33240         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33241         return r ? r : false;
33242     },
33243
33244     findRowIndex : function(n){
33245         if(!n){
33246             return false;
33247         }
33248         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
33249         return r ? r.rowIndex : false;
33250     },
33251
33252     findCellIndex : function(node){
33253         var stop = this.el.dom;
33254         while(node && node != stop){
33255             if(this.findRE.test(node.className)){
33256                 return this.getCellIndex(node);
33257             }
33258             node = node.parentNode;
33259         }
33260         return false;
33261     },
33262
33263     getColumnId : function(index){
33264         return this.cm.getColumnId(index);
33265     },
33266
33267     getSplitters : function()
33268     {
33269         if(this.splitterSelector){
33270            return Roo.DomQuery.select(this.splitterSelector);
33271         }else{
33272             return null;
33273       }
33274     },
33275
33276     getSplitter : function(index){
33277         return this.getSplitters()[index];
33278     },
33279
33280     onRowOver : function(e, t){
33281         var row;
33282         if((row = this.findRowIndex(t)) !== false){
33283             this.getRowComposite(row).addClass("x-grid-row-over");
33284         }
33285     },
33286
33287     onRowOut : function(e, t){
33288         var row;
33289         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
33290             this.getRowComposite(row).removeClass("x-grid-row-over");
33291         }
33292     },
33293
33294     renderHeaders : function(){
33295         var cm = this.cm;
33296         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
33297         var cb = [], lb = [], sb = [], lsb = [], p = {};
33298         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33299             p.cellId = "x-grid-hd-0-" + i;
33300             p.splitId = "x-grid-csplit-0-" + i;
33301             p.id = cm.getColumnId(i);
33302             p.value = cm.getColumnHeader(i) || "";
33303             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
33304             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
33305             if(!cm.isLocked(i)){
33306                 cb[cb.length] = ct.apply(p);
33307                 sb[sb.length] = st.apply(p);
33308             }else{
33309                 lb[lb.length] = ct.apply(p);
33310                 lsb[lsb.length] = st.apply(p);
33311             }
33312         }
33313         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
33314                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
33315     },
33316
33317     updateHeaders : function(){
33318         var html = this.renderHeaders();
33319         this.lockedHd.update(html[0]);
33320         this.mainHd.update(html[1]);
33321     },
33322
33323     /**
33324      * Focuses the specified row.
33325      * @param {Number} row The row index
33326      */
33327     focusRow : function(row)
33328     {
33329         //Roo.log('GridView.focusRow');
33330         var x = this.scroller.dom.scrollLeft;
33331         this.focusCell(row, 0, false);
33332         this.scroller.dom.scrollLeft = x;
33333     },
33334
33335     /**
33336      * Focuses the specified cell.
33337      * @param {Number} row The row index
33338      * @param {Number} col The column index
33339      * @param {Boolean} hscroll false to disable horizontal scrolling
33340      */
33341     focusCell : function(row, col, hscroll)
33342     {
33343         //Roo.log('GridView.focusCell');
33344         var el = this.ensureVisible(row, col, hscroll);
33345         this.focusEl.alignTo(el, "tl-tl");
33346         if(Roo.isGecko){
33347             this.focusEl.focus();
33348         }else{
33349             this.focusEl.focus.defer(1, this.focusEl);
33350         }
33351     },
33352
33353     /**
33354      * Scrolls the specified cell into view
33355      * @param {Number} row The row index
33356      * @param {Number} col The column index
33357      * @param {Boolean} hscroll false to disable horizontal scrolling
33358      */
33359     ensureVisible : function(row, col, hscroll)
33360     {
33361         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
33362         //return null; //disable for testing.
33363         if(typeof row != "number"){
33364             row = row.rowIndex;
33365         }
33366         if(row < 0 && row >= this.ds.getCount()){
33367             return  null;
33368         }
33369         col = (col !== undefined ? col : 0);
33370         var cm = this.grid.colModel;
33371         while(cm.isHidden(col)){
33372             col++;
33373         }
33374
33375         var el = this.getCell(row, col);
33376         if(!el){
33377             return null;
33378         }
33379         var c = this.scroller.dom;
33380
33381         var ctop = parseInt(el.offsetTop, 10);
33382         var cleft = parseInt(el.offsetLeft, 10);
33383         var cbot = ctop + el.offsetHeight;
33384         var cright = cleft + el.offsetWidth;
33385         
33386         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
33387         var stop = parseInt(c.scrollTop, 10);
33388         var sleft = parseInt(c.scrollLeft, 10);
33389         var sbot = stop + ch;
33390         var sright = sleft + c.clientWidth;
33391         /*
33392         Roo.log('GridView.ensureVisible:' +
33393                 ' ctop:' + ctop +
33394                 ' c.clientHeight:' + c.clientHeight +
33395                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
33396                 ' stop:' + stop +
33397                 ' cbot:' + cbot +
33398                 ' sbot:' + sbot +
33399                 ' ch:' + ch  
33400                 );
33401         */
33402         if(ctop < stop){
33403              c.scrollTop = ctop;
33404             //Roo.log("set scrolltop to ctop DISABLE?");
33405         }else if(cbot > sbot){
33406             //Roo.log("set scrolltop to cbot-ch");
33407             c.scrollTop = cbot-ch;
33408         }
33409         
33410         if(hscroll !== false){
33411             if(cleft < sleft){
33412                 c.scrollLeft = cleft;
33413             }else if(cright > sright){
33414                 c.scrollLeft = cright-c.clientWidth;
33415             }
33416         }
33417          
33418         return el;
33419     },
33420
33421     updateColumns : function(){
33422         this.grid.stopEditing();
33423         var cm = this.grid.colModel, colIds = this.getColumnIds();
33424         //var totalWidth = cm.getTotalWidth();
33425         var pos = 0;
33426         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33427             //if(cm.isHidden(i)) continue;
33428             var w = cm.getColumnWidth(i);
33429             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33430             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33431         }
33432         this.updateSplitters();
33433     },
33434
33435     generateRules : function(cm){
33436         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
33437         Roo.util.CSS.removeStyleSheet(rulesId);
33438         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33439             var cid = cm.getColumnId(i);
33440             var align = '';
33441             if(cm.config[i].align){
33442                 align = 'text-align:'+cm.config[i].align+';';
33443             }
33444             var hidden = '';
33445             if(cm.isHidden(i)){
33446                 hidden = 'display:none;';
33447             }
33448             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
33449             ruleBuf.push(
33450                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
33451                     this.hdSelector, cid, " {\n", align, width, "}\n",
33452                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
33453                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
33454         }
33455         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33456     },
33457
33458     updateSplitters : function(){
33459         var cm = this.cm, s = this.getSplitters();
33460         if(s){ // splitters not created yet
33461             var pos = 0, locked = true;
33462             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33463                 if(cm.isHidden(i)) {
33464                     continue;
33465                 }
33466                 var w = cm.getColumnWidth(i); // make sure it's a number
33467                 if(!cm.isLocked(i) && locked){
33468                     pos = 0;
33469                     locked = false;
33470                 }
33471                 pos += w;
33472                 s[i].style.left = (pos-this.splitOffset) + "px";
33473             }
33474         }
33475     },
33476
33477     handleHiddenChange : function(colModel, colIndex, hidden){
33478         if(hidden){
33479             this.hideColumn(colIndex);
33480         }else{
33481             this.unhideColumn(colIndex);
33482         }
33483     },
33484
33485     hideColumn : function(colIndex){
33486         var cid = this.getColumnId(colIndex);
33487         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
33488         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
33489         if(Roo.isSafari){
33490             this.updateHeaders();
33491         }
33492         this.updateSplitters();
33493         this.layout();
33494     },
33495
33496     unhideColumn : function(colIndex){
33497         var cid = this.getColumnId(colIndex);
33498         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
33499         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
33500
33501         if(Roo.isSafari){
33502             this.updateHeaders();
33503         }
33504         this.updateSplitters();
33505         this.layout();
33506     },
33507
33508     insertRows : function(dm, firstRow, lastRow, isUpdate){
33509         if(firstRow == 0 && lastRow == dm.getCount()-1){
33510             this.refresh();
33511         }else{
33512             if(!isUpdate){
33513                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
33514             }
33515             var s = this.getScrollState();
33516             var markup = this.renderRows(firstRow, lastRow);
33517             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
33518             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
33519             this.restoreScroll(s);
33520             if(!isUpdate){
33521                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
33522                 this.syncRowHeights(firstRow, lastRow);
33523                 this.stripeRows(firstRow);
33524                 this.layout();
33525             }
33526         }
33527     },
33528
33529     bufferRows : function(markup, target, index){
33530         var before = null, trows = target.rows, tbody = target.tBodies[0];
33531         if(index < trows.length){
33532             before = trows[index];
33533         }
33534         var b = document.createElement("div");
33535         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
33536         var rows = b.firstChild.rows;
33537         for(var i = 0, len = rows.length; i < len; i++){
33538             if(before){
33539                 tbody.insertBefore(rows[0], before);
33540             }else{
33541                 tbody.appendChild(rows[0]);
33542             }
33543         }
33544         b.innerHTML = "";
33545         b = null;
33546     },
33547
33548     deleteRows : function(dm, firstRow, lastRow){
33549         if(dm.getRowCount()<1){
33550             this.fireEvent("beforerefresh", this);
33551             this.mainBody.update("");
33552             this.lockedBody.update("");
33553             this.fireEvent("refresh", this);
33554         }else{
33555             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
33556             var bt = this.getBodyTable();
33557             var tbody = bt.firstChild;
33558             var rows = bt.rows;
33559             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
33560                 tbody.removeChild(rows[firstRow]);
33561             }
33562             this.stripeRows(firstRow);
33563             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
33564         }
33565     },
33566
33567     updateRows : function(dataSource, firstRow, lastRow){
33568         var s = this.getScrollState();
33569         this.refresh();
33570         this.restoreScroll(s);
33571     },
33572
33573     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
33574         if(!noRefresh){
33575            this.refresh();
33576         }
33577         this.updateHeaderSortState();
33578     },
33579
33580     getScrollState : function(){
33581         
33582         var sb = this.scroller.dom;
33583         return {left: sb.scrollLeft, top: sb.scrollTop};
33584     },
33585
33586     stripeRows : function(startRow){
33587         if(!this.grid.stripeRows || this.ds.getCount() < 1){
33588             return;
33589         }
33590         startRow = startRow || 0;
33591         var rows = this.getBodyTable().rows;
33592         var lrows = this.getLockedTable().rows;
33593         var cls = ' x-grid-row-alt ';
33594         for(var i = startRow, len = rows.length; i < len; i++){
33595             var row = rows[i], lrow = lrows[i];
33596             var isAlt = ((i+1) % 2 == 0);
33597             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
33598             if(isAlt == hasAlt){
33599                 continue;
33600             }
33601             if(isAlt){
33602                 row.className += " x-grid-row-alt";
33603             }else{
33604                 row.className = row.className.replace("x-grid-row-alt", "");
33605             }
33606             if(lrow){
33607                 lrow.className = row.className;
33608             }
33609         }
33610     },
33611
33612     restoreScroll : function(state){
33613         //Roo.log('GridView.restoreScroll');
33614         var sb = this.scroller.dom;
33615         sb.scrollLeft = state.left;
33616         sb.scrollTop = state.top;
33617         this.syncScroll();
33618     },
33619
33620     syncScroll : function(){
33621         //Roo.log('GridView.syncScroll');
33622         var sb = this.scroller.dom;
33623         var sh = this.mainHd.dom;
33624         var bs = this.mainBody.dom;
33625         var lv = this.lockedBody.dom;
33626         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
33627         lv.scrollTop = bs.scrollTop = sb.scrollTop;
33628     },
33629
33630     handleScroll : function(e){
33631         this.syncScroll();
33632         var sb = this.scroller.dom;
33633         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
33634         e.stopEvent();
33635     },
33636
33637     handleWheel : function(e){
33638         var d = e.getWheelDelta();
33639         this.scroller.dom.scrollTop -= d*22;
33640         // set this here to prevent jumpy scrolling on large tables
33641         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
33642         e.stopEvent();
33643     },
33644
33645     renderRows : function(startRow, endRow){
33646         // pull in all the crap needed to render rows
33647         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
33648         var colCount = cm.getColumnCount();
33649
33650         if(ds.getCount() < 1){
33651             return ["", ""];
33652         }
33653
33654         // build a map for all the columns
33655         var cs = [];
33656         for(var i = 0; i < colCount; i++){
33657             var name = cm.getDataIndex(i);
33658             cs[i] = {
33659                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
33660                 renderer : cm.getRenderer(i),
33661                 id : cm.getColumnId(i),
33662                 locked : cm.isLocked(i),
33663                 has_editor : cm.isCellEditable(i)
33664             };
33665         }
33666
33667         startRow = startRow || 0;
33668         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
33669
33670         // records to render
33671         var rs = ds.getRange(startRow, endRow);
33672
33673         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
33674     },
33675
33676     // As much as I hate to duplicate code, this was branched because FireFox really hates
33677     // [].join("") on strings. The performance difference was substantial enough to
33678     // branch this function
33679     doRender : Roo.isGecko ?
33680             function(cs, rs, ds, startRow, colCount, stripe){
33681                 var ts = this.templates, ct = ts.cell, rt = ts.row;
33682                 // buffers
33683                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
33684                 
33685                 var hasListener = this.grid.hasListener('rowclass');
33686                 var rowcfg = {};
33687                 for(var j = 0, len = rs.length; j < len; j++){
33688                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
33689                     for(var i = 0; i < colCount; i++){
33690                         c = cs[i];
33691                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
33692                         p.id = c.id;
33693                         p.css = p.attr = "";
33694                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
33695                         if(p.value == undefined || p.value === "") {
33696                             p.value = "&#160;";
33697                         }
33698                         if(c.has_editor){
33699                             p.css += ' x-grid-editable-cell';
33700                         }
33701                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
33702                             p.css +=  ' x-grid-dirty-cell';
33703                         }
33704                         var markup = ct.apply(p);
33705                         if(!c.locked){
33706                             cb+= markup;
33707                         }else{
33708                             lcb+= markup;
33709                         }
33710                     }
33711                     var alt = [];
33712                     if(stripe && ((rowIndex+1) % 2 == 0)){
33713                         alt.push("x-grid-row-alt")
33714                     }
33715                     if(r.dirty){
33716                         alt.push(  " x-grid-dirty-row");
33717                     }
33718                     rp.cells = lcb;
33719                     if(this.getRowClass){
33720                         alt.push(this.getRowClass(r, rowIndex));
33721                     }
33722                     if (hasListener) {
33723                         rowcfg = {
33724                              
33725                             record: r,
33726                             rowIndex : rowIndex,
33727                             rowClass : ''
33728                         };
33729                         this.grid.fireEvent('rowclass', this, rowcfg);
33730                         alt.push(rowcfg.rowClass);
33731                     }
33732                     rp.alt = alt.join(" ");
33733                     lbuf+= rt.apply(rp);
33734                     rp.cells = cb;
33735                     buf+=  rt.apply(rp);
33736                 }
33737                 return [lbuf, buf];
33738             } :
33739             function(cs, rs, ds, startRow, colCount, stripe){
33740                 var ts = this.templates, ct = ts.cell, rt = ts.row;
33741                 // buffers
33742                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
33743                 var hasListener = this.grid.hasListener('rowclass');
33744  
33745                 var rowcfg = {};
33746                 for(var j = 0, len = rs.length; j < len; j++){
33747                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
33748                     for(var i = 0; i < colCount; i++){
33749                         c = cs[i];
33750                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
33751                         p.id = c.id;
33752                         p.css = p.attr = "";
33753                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
33754                         if(p.value == undefined || p.value === "") {
33755                             p.value = "&#160;";
33756                         }
33757                         //Roo.log(c);
33758                          if(c.has_editor){
33759                             p.css += ' x-grid-editable-cell';
33760                         }
33761                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
33762                             p.css += ' x-grid-dirty-cell' 
33763                         }
33764                         
33765                         var markup = ct.apply(p);
33766                         if(!c.locked){
33767                             cb[cb.length] = markup;
33768                         }else{
33769                             lcb[lcb.length] = markup;
33770                         }
33771                     }
33772                     var alt = [];
33773                     if(stripe && ((rowIndex+1) % 2 == 0)){
33774                         alt.push( "x-grid-row-alt");
33775                     }
33776                     if(r.dirty){
33777                         alt.push(" x-grid-dirty-row");
33778                     }
33779                     rp.cells = lcb;
33780                     if(this.getRowClass){
33781                         alt.push( this.getRowClass(r, rowIndex));
33782                     }
33783                     if (hasListener) {
33784                         rowcfg = {
33785                              
33786                             record: r,
33787                             rowIndex : rowIndex,
33788                             rowClass : ''
33789                         };
33790                         this.grid.fireEvent('rowclass', this, rowcfg);
33791                         alt.push(rowcfg.rowClass);
33792                     }
33793                     
33794                     rp.alt = alt.join(" ");
33795                     rp.cells = lcb.join("");
33796                     lbuf[lbuf.length] = rt.apply(rp);
33797                     rp.cells = cb.join("");
33798                     buf[buf.length] =  rt.apply(rp);
33799                 }
33800                 return [lbuf.join(""), buf.join("")];
33801             },
33802
33803     renderBody : function(){
33804         var markup = this.renderRows();
33805         var bt = this.templates.body;
33806         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
33807     },
33808
33809     /**
33810      * Refreshes the grid
33811      * @param {Boolean} headersToo
33812      */
33813     refresh : function(headersToo){
33814         this.fireEvent("beforerefresh", this);
33815         this.grid.stopEditing();
33816         var result = this.renderBody();
33817         this.lockedBody.update(result[0]);
33818         this.mainBody.update(result[1]);
33819         if(headersToo === true){
33820             this.updateHeaders();
33821             this.updateColumns();
33822             this.updateSplitters();
33823             this.updateHeaderSortState();
33824         }
33825         this.syncRowHeights();
33826         this.layout();
33827         this.fireEvent("refresh", this);
33828     },
33829
33830     handleColumnMove : function(cm, oldIndex, newIndex){
33831         this.indexMap = null;
33832         var s = this.getScrollState();
33833         this.refresh(true);
33834         this.restoreScroll(s);
33835         this.afterMove(newIndex);
33836     },
33837
33838     afterMove : function(colIndex){
33839         if(this.enableMoveAnim && Roo.enableFx){
33840             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
33841         }
33842         // if multisort - fix sortOrder, and reload..
33843         if (this.grid.dataSource.multiSort) {
33844             // the we can call sort again..
33845             var dm = this.grid.dataSource;
33846             var cm = this.grid.colModel;
33847             var so = [];
33848             for(var i = 0; i < cm.config.length; i++ ) {
33849                 
33850                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
33851                     continue; // dont' bother, it's not in sort list or being set.
33852                 }
33853                 
33854                 so.push(cm.config[i].dataIndex);
33855             };
33856             dm.sortOrder = so;
33857             dm.load(dm.lastOptions);
33858             
33859             
33860         }
33861         
33862     },
33863
33864     updateCell : function(dm, rowIndex, dataIndex){
33865         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
33866         if(typeof colIndex == "undefined"){ // not present in grid
33867             return;
33868         }
33869         var cm = this.grid.colModel;
33870         var cell = this.getCell(rowIndex, colIndex);
33871         var cellText = this.getCellText(rowIndex, colIndex);
33872
33873         var p = {
33874             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
33875             id : cm.getColumnId(colIndex),
33876             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
33877         };
33878         var renderer = cm.getRenderer(colIndex);
33879         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
33880         if(typeof val == "undefined" || val === "") {
33881             val = "&#160;";
33882         }
33883         cellText.innerHTML = val;
33884         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
33885         this.syncRowHeights(rowIndex, rowIndex);
33886     },
33887
33888     calcColumnWidth : function(colIndex, maxRowsToMeasure){
33889         var maxWidth = 0;
33890         if(this.grid.autoSizeHeaders){
33891             var h = this.getHeaderCellMeasure(colIndex);
33892             maxWidth = Math.max(maxWidth, h.scrollWidth);
33893         }
33894         var tb, index;
33895         if(this.cm.isLocked(colIndex)){
33896             tb = this.getLockedTable();
33897             index = colIndex;
33898         }else{
33899             tb = this.getBodyTable();
33900             index = colIndex - this.cm.getLockedCount();
33901         }
33902         if(tb && tb.rows){
33903             var rows = tb.rows;
33904             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
33905             for(var i = 0; i < stopIndex; i++){
33906                 var cell = rows[i].childNodes[index].firstChild;
33907                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
33908             }
33909         }
33910         return maxWidth + /*margin for error in IE*/ 5;
33911     },
33912     /**
33913      * Autofit a column to its content.
33914      * @param {Number} colIndex
33915      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
33916      */
33917      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
33918          if(this.cm.isHidden(colIndex)){
33919              return; // can't calc a hidden column
33920          }
33921         if(forceMinSize){
33922             var cid = this.cm.getColumnId(colIndex);
33923             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
33924            if(this.grid.autoSizeHeaders){
33925                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
33926            }
33927         }
33928         var newWidth = this.calcColumnWidth(colIndex);
33929         this.cm.setColumnWidth(colIndex,
33930             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
33931         if(!suppressEvent){
33932             this.grid.fireEvent("columnresize", colIndex, newWidth);
33933         }
33934     },
33935
33936     /**
33937      * Autofits all columns to their content and then expands to fit any extra space in the grid
33938      */
33939      autoSizeColumns : function(){
33940         var cm = this.grid.colModel;
33941         var colCount = cm.getColumnCount();
33942         for(var i = 0; i < colCount; i++){
33943             this.autoSizeColumn(i, true, true);
33944         }
33945         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
33946             this.fitColumns();
33947         }else{
33948             this.updateColumns();
33949             this.layout();
33950         }
33951     },
33952
33953     /**
33954      * Autofits all columns to the grid's width proportionate with their current size
33955      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
33956      */
33957     fitColumns : function(reserveScrollSpace){
33958         var cm = this.grid.colModel;
33959         var colCount = cm.getColumnCount();
33960         var cols = [];
33961         var width = 0;
33962         var i, w;
33963         for (i = 0; i < colCount; i++){
33964             if(!cm.isHidden(i) && !cm.isFixed(i)){
33965                 w = cm.getColumnWidth(i);
33966                 cols.push(i);
33967                 cols.push(w);
33968                 width += w;
33969             }
33970         }
33971         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
33972         if(reserveScrollSpace){
33973             avail -= 17;
33974         }
33975         var frac = (avail - cm.getTotalWidth())/width;
33976         while (cols.length){
33977             w = cols.pop();
33978             i = cols.pop();
33979             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
33980         }
33981         this.updateColumns();
33982         this.layout();
33983     },
33984
33985     onRowSelect : function(rowIndex){
33986         var row = this.getRowComposite(rowIndex);
33987         row.addClass("x-grid-row-selected");
33988     },
33989
33990     onRowDeselect : function(rowIndex){
33991         var row = this.getRowComposite(rowIndex);
33992         row.removeClass("x-grid-row-selected");
33993     },
33994
33995     onCellSelect : function(row, col){
33996         var cell = this.getCell(row, col);
33997         if(cell){
33998             Roo.fly(cell).addClass("x-grid-cell-selected");
33999         }
34000     },
34001
34002     onCellDeselect : function(row, col){
34003         var cell = this.getCell(row, col);
34004         if(cell){
34005             Roo.fly(cell).removeClass("x-grid-cell-selected");
34006         }
34007     },
34008
34009     updateHeaderSortState : function(){
34010         
34011         // sort state can be single { field: xxx, direction : yyy}
34012         // or   { xxx=>ASC , yyy : DESC ..... }
34013         
34014         var mstate = {};
34015         if (!this.ds.multiSort) { 
34016             var state = this.ds.getSortState();
34017             if(!state){
34018                 return;
34019             }
34020             mstate[state.field] = state.direction;
34021             // FIXME... - this is not used here.. but might be elsewhere..
34022             this.sortState = state;
34023             
34024         } else {
34025             mstate = this.ds.sortToggle;
34026         }
34027         //remove existing sort classes..
34028         
34029         var sc = this.sortClasses;
34030         var hds = this.el.select(this.headerSelector).removeClass(sc);
34031         
34032         for(var f in mstate) {
34033         
34034             var sortColumn = this.cm.findColumnIndex(f);
34035             
34036             if(sortColumn != -1){
34037                 var sortDir = mstate[f];        
34038                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
34039             }
34040         }
34041         
34042          
34043         
34044     },
34045
34046
34047     handleHeaderClick : function(g, index,e){
34048         
34049         Roo.log("header click");
34050         
34051         if (Roo.isTouch) {
34052             // touch events on header are handled by context
34053             this.handleHdCtx(g,index,e);
34054             return;
34055         }
34056         
34057         
34058         if(this.headersDisabled){
34059             return;
34060         }
34061         var dm = g.dataSource, cm = g.colModel;
34062         if(!cm.isSortable(index)){
34063             return;
34064         }
34065         g.stopEditing();
34066         
34067         if (dm.multiSort) {
34068             // update the sortOrder
34069             var so = [];
34070             for(var i = 0; i < cm.config.length; i++ ) {
34071                 
34072                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
34073                     continue; // dont' bother, it's not in sort list or being set.
34074                 }
34075                 
34076                 so.push(cm.config[i].dataIndex);
34077             };
34078             dm.sortOrder = so;
34079         }
34080         
34081         
34082         dm.sort(cm.getDataIndex(index));
34083     },
34084
34085
34086     destroy : function(){
34087         if(this.colMenu){
34088             this.colMenu.removeAll();
34089             Roo.menu.MenuMgr.unregister(this.colMenu);
34090             this.colMenu.getEl().remove();
34091             delete this.colMenu;
34092         }
34093         if(this.hmenu){
34094             this.hmenu.removeAll();
34095             Roo.menu.MenuMgr.unregister(this.hmenu);
34096             this.hmenu.getEl().remove();
34097             delete this.hmenu;
34098         }
34099         if(this.grid.enableColumnMove){
34100             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34101             if(dds){
34102                 for(var dd in dds){
34103                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
34104                         var elid = dds[dd].dragElId;
34105                         dds[dd].unreg();
34106                         Roo.get(elid).remove();
34107                     } else if(dds[dd].config.isTarget){
34108                         dds[dd].proxyTop.remove();
34109                         dds[dd].proxyBottom.remove();
34110                         dds[dd].unreg();
34111                     }
34112                     if(Roo.dd.DDM.locationCache[dd]){
34113                         delete Roo.dd.DDM.locationCache[dd];
34114                     }
34115                 }
34116                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34117             }
34118         }
34119         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
34120         this.bind(null, null);
34121         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
34122     },
34123
34124     handleLockChange : function(){
34125         this.refresh(true);
34126     },
34127
34128     onDenyColumnLock : function(){
34129
34130     },
34131
34132     onDenyColumnHide : function(){
34133
34134     },
34135
34136     handleHdMenuClick : function(item){
34137         var index = this.hdCtxIndex;
34138         var cm = this.cm, ds = this.ds;
34139         switch(item.id){
34140             case "asc":
34141                 ds.sort(cm.getDataIndex(index), "ASC");
34142                 break;
34143             case "desc":
34144                 ds.sort(cm.getDataIndex(index), "DESC");
34145                 break;
34146             case "lock":
34147                 var lc = cm.getLockedCount();
34148                 if(cm.getColumnCount(true) <= lc+1){
34149                     this.onDenyColumnLock();
34150                     return;
34151                 }
34152                 if(lc != index){
34153                     cm.setLocked(index, true, true);
34154                     cm.moveColumn(index, lc);
34155                     this.grid.fireEvent("columnmove", index, lc);
34156                 }else{
34157                     cm.setLocked(index, true);
34158                 }
34159             break;
34160             case "unlock":
34161                 var lc = cm.getLockedCount();
34162                 if((lc-1) != index){
34163                     cm.setLocked(index, false, true);
34164                     cm.moveColumn(index, lc-1);
34165                     this.grid.fireEvent("columnmove", index, lc-1);
34166                 }else{
34167                     cm.setLocked(index, false);
34168                 }
34169             break;
34170             case 'wider': // used to expand cols on touch..
34171             case 'narrow':
34172                 var cw = cm.getColumnWidth(index);
34173                 cw += (item.id == 'wider' ? 1 : -1) * 50;
34174                 cw = Math.max(0, cw);
34175                 cw = Math.min(cw,4000);
34176                 cm.setColumnWidth(index, cw);
34177                 break;
34178                 
34179             default:
34180                 index = cm.getIndexById(item.id.substr(4));
34181                 if(index != -1){
34182                     if(item.checked && cm.getColumnCount(true) <= 1){
34183                         this.onDenyColumnHide();
34184                         return false;
34185                     }
34186                     cm.setHidden(index, item.checked);
34187                 }
34188         }
34189         return true;
34190     },
34191
34192     beforeColMenuShow : function(){
34193         var cm = this.cm,  colCount = cm.getColumnCount();
34194         this.colMenu.removeAll();
34195         for(var i = 0; i < colCount; i++){
34196             this.colMenu.add(new Roo.menu.CheckItem({
34197                 id: "col-"+cm.getColumnId(i),
34198                 text: cm.getColumnHeader(i),
34199                 checked: !cm.isHidden(i),
34200                 hideOnClick:false
34201             }));
34202         }
34203     },
34204
34205     handleHdCtx : function(g, index, e){
34206         e.stopEvent();
34207         var hd = this.getHeaderCell(index);
34208         this.hdCtxIndex = index;
34209         var ms = this.hmenu.items, cm = this.cm;
34210         ms.get("asc").setDisabled(!cm.isSortable(index));
34211         ms.get("desc").setDisabled(!cm.isSortable(index));
34212         if(this.grid.enableColLock !== false){
34213             ms.get("lock").setDisabled(cm.isLocked(index));
34214             ms.get("unlock").setDisabled(!cm.isLocked(index));
34215         }
34216         this.hmenu.show(hd, "tl-bl");
34217     },
34218
34219     handleHdOver : function(e){
34220         var hd = this.findHeaderCell(e.getTarget());
34221         if(hd && !this.headersDisabled){
34222             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
34223                this.fly(hd).addClass("x-grid-hd-over");
34224             }
34225         }
34226     },
34227
34228     handleHdOut : function(e){
34229         var hd = this.findHeaderCell(e.getTarget());
34230         if(hd){
34231             this.fly(hd).removeClass("x-grid-hd-over");
34232         }
34233     },
34234
34235     handleSplitDblClick : function(e, t){
34236         var i = this.getCellIndex(t);
34237         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
34238             this.autoSizeColumn(i, true);
34239             this.layout();
34240         }
34241     },
34242
34243     render : function(){
34244
34245         var cm = this.cm;
34246         var colCount = cm.getColumnCount();
34247
34248         if(this.grid.monitorWindowResize === true){
34249             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34250         }
34251         var header = this.renderHeaders();
34252         var body = this.templates.body.apply({rows:""});
34253         var html = this.templates.master.apply({
34254             lockedBody: body,
34255             body: body,
34256             lockedHeader: header[0],
34257             header: header[1]
34258         });
34259
34260         //this.updateColumns();
34261
34262         this.grid.getGridEl().dom.innerHTML = html;
34263
34264         this.initElements();
34265         
34266         // a kludge to fix the random scolling effect in webkit
34267         this.el.on("scroll", function() {
34268             this.el.dom.scrollTop=0; // hopefully not recursive..
34269         },this);
34270
34271         this.scroller.on("scroll", this.handleScroll, this);
34272         this.lockedBody.on("mousewheel", this.handleWheel, this);
34273         this.mainBody.on("mousewheel", this.handleWheel, this);
34274
34275         this.mainHd.on("mouseover", this.handleHdOver, this);
34276         this.mainHd.on("mouseout", this.handleHdOut, this);
34277         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
34278                 {delegate: "."+this.splitClass});
34279
34280         this.lockedHd.on("mouseover", this.handleHdOver, this);
34281         this.lockedHd.on("mouseout", this.handleHdOut, this);
34282         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
34283                 {delegate: "."+this.splitClass});
34284
34285         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
34286             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34287         }
34288
34289         this.updateSplitters();
34290
34291         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
34292             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34293             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34294         }
34295
34296         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
34297             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
34298             this.hmenu.add(
34299                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
34300                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
34301             );
34302             if(this.grid.enableColLock !== false){
34303                 this.hmenu.add('-',
34304                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
34305                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
34306                 );
34307             }
34308             if (Roo.isTouch) {
34309                  this.hmenu.add('-',
34310                     {id:"wider", text: this.columnsWiderText},
34311                     {id:"narrow", text: this.columnsNarrowText }
34312                 );
34313                 
34314                  
34315             }
34316             
34317             if(this.grid.enableColumnHide !== false){
34318
34319                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
34320                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
34321                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
34322
34323                 this.hmenu.add('-',
34324                     {id:"columns", text: this.columnsText, menu: this.colMenu}
34325                 );
34326             }
34327             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
34328
34329             this.grid.on("headercontextmenu", this.handleHdCtx, this);
34330         }
34331
34332         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
34333             this.dd = new Roo.grid.GridDragZone(this.grid, {
34334                 ddGroup : this.grid.ddGroup || 'GridDD'
34335             });
34336             
34337         }
34338
34339         /*
34340         for(var i = 0; i < colCount; i++){
34341             if(cm.isHidden(i)){
34342                 this.hideColumn(i);
34343             }
34344             if(cm.config[i].align){
34345                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
34346                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
34347             }
34348         }*/
34349         
34350         this.updateHeaderSortState();
34351
34352         this.beforeInitialResize();
34353         this.layout(true);
34354
34355         // two part rendering gives faster view to the user
34356         this.renderPhase2.defer(1, this);
34357     },
34358
34359     renderPhase2 : function(){
34360         // render the rows now
34361         this.refresh();
34362         if(this.grid.autoSizeColumns){
34363             this.autoSizeColumns();
34364         }
34365     },
34366
34367     beforeInitialResize : function(){
34368
34369     },
34370
34371     onColumnSplitterMoved : function(i, w){
34372         this.userResized = true;
34373         var cm = this.grid.colModel;
34374         cm.setColumnWidth(i, w, true);
34375         var cid = cm.getColumnId(i);
34376         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34377         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34378         this.updateSplitters();
34379         this.layout();
34380         this.grid.fireEvent("columnresize", i, w);
34381     },
34382
34383     syncRowHeights : function(startIndex, endIndex){
34384         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
34385             startIndex = startIndex || 0;
34386             var mrows = this.getBodyTable().rows;
34387             var lrows = this.getLockedTable().rows;
34388             var len = mrows.length-1;
34389             endIndex = Math.min(endIndex || len, len);
34390             for(var i = startIndex; i <= endIndex; i++){
34391                 var m = mrows[i], l = lrows[i];
34392                 var h = Math.max(m.offsetHeight, l.offsetHeight);
34393                 m.style.height = l.style.height = h + "px";
34394             }
34395         }
34396     },
34397
34398     layout : function(initialRender, is2ndPass){
34399         var g = this.grid;
34400         var auto = g.autoHeight;
34401         var scrollOffset = 16;
34402         var c = g.getGridEl(), cm = this.cm,
34403                 expandCol = g.autoExpandColumn,
34404                 gv = this;
34405         //c.beginMeasure();
34406
34407         if(!c.dom.offsetWidth){ // display:none?
34408             if(initialRender){
34409                 this.lockedWrap.show();
34410                 this.mainWrap.show();
34411             }
34412             return;
34413         }
34414
34415         var hasLock = this.cm.isLocked(0);
34416
34417         var tbh = this.headerPanel.getHeight();
34418         var bbh = this.footerPanel.getHeight();
34419
34420         if(auto){
34421             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
34422             var newHeight = ch + c.getBorderWidth("tb");
34423             if(g.maxHeight){
34424                 newHeight = Math.min(g.maxHeight, newHeight);
34425             }
34426             c.setHeight(newHeight);
34427         }
34428
34429         if(g.autoWidth){
34430             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
34431         }
34432
34433         var s = this.scroller;
34434
34435         var csize = c.getSize(true);
34436
34437         this.el.setSize(csize.width, csize.height);
34438
34439         this.headerPanel.setWidth(csize.width);
34440         this.footerPanel.setWidth(csize.width);
34441
34442         var hdHeight = this.mainHd.getHeight();
34443         var vw = csize.width;
34444         var vh = csize.height - (tbh + bbh);
34445
34446         s.setSize(vw, vh);
34447
34448         var bt = this.getBodyTable();
34449         
34450         if(cm.getLockedCount() == cm.config.length){
34451             bt = this.getLockedTable();
34452         }
34453         
34454         var ltWidth = hasLock ?
34455                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
34456
34457         var scrollHeight = bt.offsetHeight;
34458         var scrollWidth = ltWidth + bt.offsetWidth;
34459         var vscroll = false, hscroll = false;
34460
34461         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
34462
34463         var lw = this.lockedWrap, mw = this.mainWrap;
34464         var lb = this.lockedBody, mb = this.mainBody;
34465
34466         setTimeout(function(){
34467             var t = s.dom.offsetTop;
34468             var w = s.dom.clientWidth,
34469                 h = s.dom.clientHeight;
34470
34471             lw.setTop(t);
34472             lw.setSize(ltWidth, h);
34473
34474             mw.setLeftTop(ltWidth, t);
34475             mw.setSize(w-ltWidth, h);
34476
34477             lb.setHeight(h-hdHeight);
34478             mb.setHeight(h-hdHeight);
34479
34480             if(is2ndPass !== true && !gv.userResized && expandCol){
34481                 // high speed resize without full column calculation
34482                 
34483                 var ci = cm.getIndexById(expandCol);
34484                 if (ci < 0) {
34485                     ci = cm.findColumnIndex(expandCol);
34486                 }
34487                 ci = Math.max(0, ci); // make sure it's got at least the first col.
34488                 var expandId = cm.getColumnId(ci);
34489                 var  tw = cm.getTotalWidth(false);
34490                 var currentWidth = cm.getColumnWidth(ci);
34491                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
34492                 if(currentWidth != cw){
34493                     cm.setColumnWidth(ci, cw, true);
34494                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34495                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34496                     gv.updateSplitters();
34497                     gv.layout(false, true);
34498                 }
34499             }
34500
34501             if(initialRender){
34502                 lw.show();
34503                 mw.show();
34504             }
34505             //c.endMeasure();
34506         }, 10);
34507     },
34508
34509     onWindowResize : function(){
34510         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
34511             return;
34512         }
34513         this.layout();
34514     },
34515
34516     appendFooter : function(parentEl){
34517         return null;
34518     },
34519
34520     sortAscText : "Sort Ascending",
34521     sortDescText : "Sort Descending",
34522     lockText : "Lock Column",
34523     unlockText : "Unlock Column",
34524     columnsText : "Columns",
34525  
34526     columnsWiderText : "Wider",
34527     columnsNarrowText : "Thinner"
34528 });
34529
34530
34531 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
34532     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
34533     this.proxy.el.addClass('x-grid3-col-dd');
34534 };
34535
34536 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
34537     handleMouseDown : function(e){
34538
34539     },
34540
34541     callHandleMouseDown : function(e){
34542         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
34543     }
34544 });
34545 /*
34546  * Based on:
34547  * Ext JS Library 1.1.1
34548  * Copyright(c) 2006-2007, Ext JS, LLC.
34549  *
34550  * Originally Released Under LGPL - original licence link has changed is not relivant.
34551  *
34552  * Fork - LGPL
34553  * <script type="text/javascript">
34554  */
34555  
34556 // private
34557 // This is a support class used internally by the Grid components
34558 Roo.grid.SplitDragZone = function(grid, hd, hd2){
34559     this.grid = grid;
34560     this.view = grid.getView();
34561     this.proxy = this.view.resizeProxy;
34562     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
34563         "gridSplitters" + this.grid.getGridEl().id, {
34564         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
34565     });
34566     this.setHandleElId(Roo.id(hd));
34567     this.setOuterHandleElId(Roo.id(hd2));
34568     this.scroll = false;
34569 };
34570 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
34571     fly: Roo.Element.fly,
34572
34573     b4StartDrag : function(x, y){
34574         this.view.headersDisabled = true;
34575         this.proxy.setHeight(this.view.mainWrap.getHeight());
34576         var w = this.cm.getColumnWidth(this.cellIndex);
34577         var minw = Math.max(w-this.grid.minColumnWidth, 0);
34578         this.resetConstraints();
34579         this.setXConstraint(minw, 1000);
34580         this.setYConstraint(0, 0);
34581         this.minX = x - minw;
34582         this.maxX = x + 1000;
34583         this.startPos = x;
34584         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
34585     },
34586
34587
34588     handleMouseDown : function(e){
34589         ev = Roo.EventObject.setEvent(e);
34590         var t = this.fly(ev.getTarget());
34591         if(t.hasClass("x-grid-split")){
34592             this.cellIndex = this.view.getCellIndex(t.dom);
34593             this.split = t.dom;
34594             this.cm = this.grid.colModel;
34595             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
34596                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
34597             }
34598         }
34599     },
34600
34601     endDrag : function(e){
34602         this.view.headersDisabled = false;
34603         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
34604         var diff = endX - this.startPos;
34605         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
34606     },
34607
34608     autoOffset : function(){
34609         this.setDelta(0,0);
34610     }
34611 });/*
34612  * Based on:
34613  * Ext JS Library 1.1.1
34614  * Copyright(c) 2006-2007, Ext JS, LLC.
34615  *
34616  * Originally Released Under LGPL - original licence link has changed is not relivant.
34617  *
34618  * Fork - LGPL
34619  * <script type="text/javascript">
34620  */
34621  
34622 // private
34623 // This is a support class used internally by the Grid components
34624 Roo.grid.GridDragZone = function(grid, config){
34625     this.view = grid.getView();
34626     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
34627     if(this.view.lockedBody){
34628         this.setHandleElId(Roo.id(this.view.mainBody.dom));
34629         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
34630     }
34631     this.scroll = false;
34632     this.grid = grid;
34633     this.ddel = document.createElement('div');
34634     this.ddel.className = 'x-grid-dd-wrap';
34635 };
34636
34637 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
34638     ddGroup : "GridDD",
34639
34640     getDragData : function(e){
34641         var t = Roo.lib.Event.getTarget(e);
34642         var rowIndex = this.view.findRowIndex(t);
34643         var sm = this.grid.selModel;
34644             
34645         //Roo.log(rowIndex);
34646         
34647         if (sm.getSelectedCell) {
34648             // cell selection..
34649             if (!sm.getSelectedCell()) {
34650                 return false;
34651             }
34652             if (rowIndex != sm.getSelectedCell()[0]) {
34653                 return false;
34654             }
34655         
34656         }
34657         
34658         if(rowIndex !== false){
34659             
34660             // if editorgrid.. 
34661             
34662             
34663             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
34664                
34665             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
34666               //  
34667             //}
34668             if (e.hasModifier()){
34669                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
34670             }
34671             
34672             Roo.log("getDragData");
34673             
34674             return {
34675                 grid: this.grid,
34676                 ddel: this.ddel,
34677                 rowIndex: rowIndex,
34678                 selections:sm.getSelections ? sm.getSelections() : (
34679                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
34680                 )
34681             };
34682         }
34683         return false;
34684     },
34685
34686     onInitDrag : function(e){
34687         var data = this.dragData;
34688         this.ddel.innerHTML = this.grid.getDragDropText();
34689         this.proxy.update(this.ddel);
34690         // fire start drag?
34691     },
34692
34693     afterRepair : function(){
34694         this.dragging = false;
34695     },
34696
34697     getRepairXY : function(e, data){
34698         return false;
34699     },
34700
34701     onEndDrag : function(data, e){
34702         // fire end drag?
34703     },
34704
34705     onValidDrop : function(dd, e, id){
34706         // fire drag drop?
34707         this.hideProxy();
34708     },
34709
34710     beforeInvalidDrop : function(e, id){
34711
34712     }
34713 });/*
34714  * Based on:
34715  * Ext JS Library 1.1.1
34716  * Copyright(c) 2006-2007, Ext JS, LLC.
34717  *
34718  * Originally Released Under LGPL - original licence link has changed is not relivant.
34719  *
34720  * Fork - LGPL
34721  * <script type="text/javascript">
34722  */
34723  
34724
34725 /**
34726  * @class Roo.grid.ColumnModel
34727  * @extends Roo.util.Observable
34728  * This is the default implementation of a ColumnModel used by the Grid. It defines
34729  * the columns in the grid.
34730  * <br>Usage:<br>
34731  <pre><code>
34732  var colModel = new Roo.grid.ColumnModel([
34733         {header: "Ticker", width: 60, sortable: true, locked: true},
34734         {header: "Company Name", width: 150, sortable: true},
34735         {header: "Market Cap.", width: 100, sortable: true},
34736         {header: "$ Sales", width: 100, sortable: true, renderer: money},
34737         {header: "Employees", width: 100, sortable: true, resizable: false}
34738  ]);
34739  </code></pre>
34740  * <p>
34741  
34742  * The config options listed for this class are options which may appear in each
34743  * individual column definition.
34744  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
34745  * @constructor
34746  * @param {Object} config An Array of column config objects. See this class's
34747  * config objects for details.
34748 */
34749 Roo.grid.ColumnModel = function(config){
34750         /**
34751      * The config passed into the constructor
34752      */
34753     this.config = config;
34754     this.lookup = {};
34755
34756     // if no id, create one
34757     // if the column does not have a dataIndex mapping,
34758     // map it to the order it is in the config
34759     for(var i = 0, len = config.length; i < len; i++){
34760         var c = config[i];
34761         if(typeof c.dataIndex == "undefined"){
34762             c.dataIndex = i;
34763         }
34764         if(typeof c.renderer == "string"){
34765             c.renderer = Roo.util.Format[c.renderer];
34766         }
34767         if(typeof c.id == "undefined"){
34768             c.id = Roo.id();
34769         }
34770         if(c.editor && c.editor.xtype){
34771             c.editor  = Roo.factory(c.editor, Roo.grid);
34772         }
34773         if(c.editor && c.editor.isFormField){
34774             c.editor = new Roo.grid.GridEditor(c.editor);
34775         }
34776         this.lookup[c.id] = c;
34777     }
34778
34779     /**
34780      * The width of columns which have no width specified (defaults to 100)
34781      * @type Number
34782      */
34783     this.defaultWidth = 100;
34784
34785     /**
34786      * Default sortable of columns which have no sortable specified (defaults to false)
34787      * @type Boolean
34788      */
34789     this.defaultSortable = false;
34790
34791     this.addEvents({
34792         /**
34793              * @event widthchange
34794              * Fires when the width of a column changes.
34795              * @param {ColumnModel} this
34796              * @param {Number} columnIndex The column index
34797              * @param {Number} newWidth The new width
34798              */
34799             "widthchange": true,
34800         /**
34801              * @event headerchange
34802              * Fires when the text of a header changes.
34803              * @param {ColumnModel} this
34804              * @param {Number} columnIndex The column index
34805              * @param {Number} newText The new header text
34806              */
34807             "headerchange": true,
34808         /**
34809              * @event hiddenchange
34810              * Fires when a column is hidden or "unhidden".
34811              * @param {ColumnModel} this
34812              * @param {Number} columnIndex The column index
34813              * @param {Boolean} hidden true if hidden, false otherwise
34814              */
34815             "hiddenchange": true,
34816             /**
34817          * @event columnmoved
34818          * Fires when a column is moved.
34819          * @param {ColumnModel} this
34820          * @param {Number} oldIndex
34821          * @param {Number} newIndex
34822          */
34823         "columnmoved" : true,
34824         /**
34825          * @event columlockchange
34826          * Fires when a column's locked state is changed
34827          * @param {ColumnModel} this
34828          * @param {Number} colIndex
34829          * @param {Boolean} locked true if locked
34830          */
34831         "columnlockchange" : true
34832     });
34833     Roo.grid.ColumnModel.superclass.constructor.call(this);
34834 };
34835 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
34836     /**
34837      * @cfg {String} header The header text to display in the Grid view.
34838      */
34839     /**
34840      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
34841      * {@link Roo.data.Record} definition from which to draw the column's value. If not
34842      * specified, the column's index is used as an index into the Record's data Array.
34843      */
34844     /**
34845      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
34846      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
34847      */
34848     /**
34849      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
34850      * Defaults to the value of the {@link #defaultSortable} property.
34851      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
34852      */
34853     /**
34854      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
34855      */
34856     /**
34857      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
34858      */
34859     /**
34860      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
34861      */
34862     /**
34863      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
34864      */
34865     /**
34866      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
34867      * given the cell's data value. See {@link #setRenderer}. If not specified, the
34868      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
34869      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
34870      */
34871        /**
34872      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
34873      */
34874     /**
34875      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
34876      */
34877     /**
34878      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
34879      */
34880     /**
34881      * @cfg {String} cursor (Optional)
34882      */
34883     /**
34884      * @cfg {String} tooltip (Optional)
34885      */
34886     /**
34887      * @cfg {Number} xs (Optional)
34888      */
34889     /**
34890      * @cfg {Number} sm (Optional)
34891      */
34892     /**
34893      * @cfg {Number} md (Optional)
34894      */
34895     /**
34896      * @cfg {Number} lg (Optional)
34897      */
34898     /**
34899      * Returns the id of the column at the specified index.
34900      * @param {Number} index The column index
34901      * @return {String} the id
34902      */
34903     getColumnId : function(index){
34904         return this.config[index].id;
34905     },
34906
34907     /**
34908      * Returns the column for a specified id.
34909      * @param {String} id The column id
34910      * @return {Object} the column
34911      */
34912     getColumnById : function(id){
34913         return this.lookup[id];
34914     },
34915
34916     
34917     /**
34918      * Returns the column for a specified dataIndex.
34919      * @param {String} dataIndex The column dataIndex
34920      * @return {Object|Boolean} the column or false if not found
34921      */
34922     getColumnByDataIndex: function(dataIndex){
34923         var index = this.findColumnIndex(dataIndex);
34924         return index > -1 ? this.config[index] : false;
34925     },
34926     
34927     /**
34928      * Returns the index for a specified column id.
34929      * @param {String} id The column id
34930      * @return {Number} the index, or -1 if not found
34931      */
34932     getIndexById : function(id){
34933         for(var i = 0, len = this.config.length; i < len; i++){
34934             if(this.config[i].id == id){
34935                 return i;
34936             }
34937         }
34938         return -1;
34939     },
34940     
34941     /**
34942      * Returns the index for a specified column dataIndex.
34943      * @param {String} dataIndex The column dataIndex
34944      * @return {Number} the index, or -1 if not found
34945      */
34946     
34947     findColumnIndex : function(dataIndex){
34948         for(var i = 0, len = this.config.length; i < len; i++){
34949             if(this.config[i].dataIndex == dataIndex){
34950                 return i;
34951             }
34952         }
34953         return -1;
34954     },
34955     
34956     
34957     moveColumn : function(oldIndex, newIndex){
34958         var c = this.config[oldIndex];
34959         this.config.splice(oldIndex, 1);
34960         this.config.splice(newIndex, 0, c);
34961         this.dataMap = null;
34962         this.fireEvent("columnmoved", this, oldIndex, newIndex);
34963     },
34964
34965     isLocked : function(colIndex){
34966         return this.config[colIndex].locked === true;
34967     },
34968
34969     setLocked : function(colIndex, value, suppressEvent){
34970         if(this.isLocked(colIndex) == value){
34971             return;
34972         }
34973         this.config[colIndex].locked = value;
34974         if(!suppressEvent){
34975             this.fireEvent("columnlockchange", this, colIndex, value);
34976         }
34977     },
34978
34979     getTotalLockedWidth : function(){
34980         var totalWidth = 0;
34981         for(var i = 0; i < this.config.length; i++){
34982             if(this.isLocked(i) && !this.isHidden(i)){
34983                 this.totalWidth += this.getColumnWidth(i);
34984             }
34985         }
34986         return totalWidth;
34987     },
34988
34989     getLockedCount : function(){
34990         for(var i = 0, len = this.config.length; i < len; i++){
34991             if(!this.isLocked(i)){
34992                 return i;
34993             }
34994         }
34995         
34996         return this.config.length;
34997     },
34998
34999     /**
35000      * Returns the number of columns.
35001      * @return {Number}
35002      */
35003     getColumnCount : function(visibleOnly){
35004         if(visibleOnly === true){
35005             var c = 0;
35006             for(var i = 0, len = this.config.length; i < len; i++){
35007                 if(!this.isHidden(i)){
35008                     c++;
35009                 }
35010             }
35011             return c;
35012         }
35013         return this.config.length;
35014     },
35015
35016     /**
35017      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
35018      * @param {Function} fn
35019      * @param {Object} scope (optional)
35020      * @return {Array} result
35021      */
35022     getColumnsBy : function(fn, scope){
35023         var r = [];
35024         for(var i = 0, len = this.config.length; i < len; i++){
35025             var c = this.config[i];
35026             if(fn.call(scope||this, c, i) === true){
35027                 r[r.length] = c;
35028             }
35029         }
35030         return r;
35031     },
35032
35033     /**
35034      * Returns true if the specified column is sortable.
35035      * @param {Number} col The column index
35036      * @return {Boolean}
35037      */
35038     isSortable : function(col){
35039         if(typeof this.config[col].sortable == "undefined"){
35040             return this.defaultSortable;
35041         }
35042         return this.config[col].sortable;
35043     },
35044
35045     /**
35046      * Returns the rendering (formatting) function defined for the column.
35047      * @param {Number} col The column index.
35048      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
35049      */
35050     getRenderer : function(col){
35051         if(!this.config[col].renderer){
35052             return Roo.grid.ColumnModel.defaultRenderer;
35053         }
35054         return this.config[col].renderer;
35055     },
35056
35057     /**
35058      * Sets the rendering (formatting) function for a column.
35059      * @param {Number} col The column index
35060      * @param {Function} fn The function to use to process the cell's raw data
35061      * to return HTML markup for the grid view. The render function is called with
35062      * the following parameters:<ul>
35063      * <li>Data value.</li>
35064      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
35065      * <li>css A CSS style string to apply to the table cell.</li>
35066      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
35067      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
35068      * <li>Row index</li>
35069      * <li>Column index</li>
35070      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
35071      */
35072     setRenderer : function(col, fn){
35073         this.config[col].renderer = fn;
35074     },
35075
35076     /**
35077      * Returns the width for the specified column.
35078      * @param {Number} col The column index
35079      * @return {Number}
35080      */
35081     getColumnWidth : function(col){
35082         return this.config[col].width * 1 || this.defaultWidth;
35083     },
35084
35085     /**
35086      * Sets the width for a column.
35087      * @param {Number} col The column index
35088      * @param {Number} width The new width
35089      */
35090     setColumnWidth : function(col, width, suppressEvent){
35091         this.config[col].width = width;
35092         this.totalWidth = null;
35093         if(!suppressEvent){
35094              this.fireEvent("widthchange", this, col, width);
35095         }
35096     },
35097
35098     /**
35099      * Returns the total width of all columns.
35100      * @param {Boolean} includeHidden True to include hidden column widths
35101      * @return {Number}
35102      */
35103     getTotalWidth : function(includeHidden){
35104         if(!this.totalWidth){
35105             this.totalWidth = 0;
35106             for(var i = 0, len = this.config.length; i < len; i++){
35107                 if(includeHidden || !this.isHidden(i)){
35108                     this.totalWidth += this.getColumnWidth(i);
35109                 }
35110             }
35111         }
35112         return this.totalWidth;
35113     },
35114
35115     /**
35116      * Returns the header for the specified column.
35117      * @param {Number} col The column index
35118      * @return {String}
35119      */
35120     getColumnHeader : function(col){
35121         return this.config[col].header;
35122     },
35123
35124     /**
35125      * Sets the header for a column.
35126      * @param {Number} col The column index
35127      * @param {String} header The new header
35128      */
35129     setColumnHeader : function(col, header){
35130         this.config[col].header = header;
35131         this.fireEvent("headerchange", this, col, header);
35132     },
35133
35134     /**
35135      * Returns the tooltip for the specified column.
35136      * @param {Number} col The column index
35137      * @return {String}
35138      */
35139     getColumnTooltip : function(col){
35140             return this.config[col].tooltip;
35141     },
35142     /**
35143      * Sets the tooltip for a column.
35144      * @param {Number} col The column index
35145      * @param {String} tooltip The new tooltip
35146      */
35147     setColumnTooltip : function(col, tooltip){
35148             this.config[col].tooltip = tooltip;
35149     },
35150
35151     /**
35152      * Returns the dataIndex for the specified column.
35153      * @param {Number} col The column index
35154      * @return {Number}
35155      */
35156     getDataIndex : function(col){
35157         return this.config[col].dataIndex;
35158     },
35159
35160     /**
35161      * Sets the dataIndex for a column.
35162      * @param {Number} col The column index
35163      * @param {Number} dataIndex The new dataIndex
35164      */
35165     setDataIndex : function(col, dataIndex){
35166         this.config[col].dataIndex = dataIndex;
35167     },
35168
35169     
35170     
35171     /**
35172      * Returns true if the cell is editable.
35173      * @param {Number} colIndex The column index
35174      * @param {Number} rowIndex The row index - this is nto actually used..?
35175      * @return {Boolean}
35176      */
35177     isCellEditable : function(colIndex, rowIndex){
35178         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
35179     },
35180
35181     /**
35182      * Returns the editor defined for the cell/column.
35183      * return false or null to disable editing.
35184      * @param {Number} colIndex The column index
35185      * @param {Number} rowIndex The row index
35186      * @return {Object}
35187      */
35188     getCellEditor : function(colIndex, rowIndex){
35189         return this.config[colIndex].editor;
35190     },
35191
35192     /**
35193      * Sets if a column is editable.
35194      * @param {Number} col The column index
35195      * @param {Boolean} editable True if the column is editable
35196      */
35197     setEditable : function(col, editable){
35198         this.config[col].editable = editable;
35199     },
35200
35201
35202     /**
35203      * Returns true if the column is hidden.
35204      * @param {Number} colIndex The column index
35205      * @return {Boolean}
35206      */
35207     isHidden : function(colIndex){
35208         return this.config[colIndex].hidden;
35209     },
35210
35211
35212     /**
35213      * Returns true if the column width cannot be changed
35214      */
35215     isFixed : function(colIndex){
35216         return this.config[colIndex].fixed;
35217     },
35218
35219     /**
35220      * Returns true if the column can be resized
35221      * @return {Boolean}
35222      */
35223     isResizable : function(colIndex){
35224         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
35225     },
35226     /**
35227      * Sets if a column is hidden.
35228      * @param {Number} colIndex The column index
35229      * @param {Boolean} hidden True if the column is hidden
35230      */
35231     setHidden : function(colIndex, hidden){
35232         this.config[colIndex].hidden = hidden;
35233         this.totalWidth = null;
35234         this.fireEvent("hiddenchange", this, colIndex, hidden);
35235     },
35236
35237     /**
35238      * Sets the editor for a column.
35239      * @param {Number} col The column index
35240      * @param {Object} editor The editor object
35241      */
35242     setEditor : function(col, editor){
35243         this.config[col].editor = editor;
35244     }
35245 });
35246
35247 Roo.grid.ColumnModel.defaultRenderer = function(value)
35248 {
35249     if(typeof value == "object") {
35250         return value;
35251     }
35252         if(typeof value == "string" && value.length < 1){
35253             return "&#160;";
35254         }
35255     
35256         return String.format("{0}", value);
35257 };
35258
35259 // Alias for backwards compatibility
35260 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
35261 /*
35262  * Based on:
35263  * Ext JS Library 1.1.1
35264  * Copyright(c) 2006-2007, Ext JS, LLC.
35265  *
35266  * Originally Released Under LGPL - original licence link has changed is not relivant.
35267  *
35268  * Fork - LGPL
35269  * <script type="text/javascript">
35270  */
35271
35272 /**
35273  * @class Roo.grid.AbstractSelectionModel
35274  * @extends Roo.util.Observable
35275  * Abstract base class for grid SelectionModels.  It provides the interface that should be
35276  * implemented by descendant classes.  This class should not be directly instantiated.
35277  * @constructor
35278  */
35279 Roo.grid.AbstractSelectionModel = function(){
35280     this.locked = false;
35281     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
35282 };
35283
35284 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
35285     /** @ignore Called by the grid automatically. Do not call directly. */
35286     init : function(grid){
35287         this.grid = grid;
35288         this.initEvents();
35289     },
35290
35291     /**
35292      * Locks the selections.
35293      */
35294     lock : function(){
35295         this.locked = true;
35296     },
35297
35298     /**
35299      * Unlocks the selections.
35300      */
35301     unlock : function(){
35302         this.locked = false;
35303     },
35304
35305     /**
35306      * Returns true if the selections are locked.
35307      * @return {Boolean}
35308      */
35309     isLocked : function(){
35310         return this.locked;
35311     }
35312 });/*
35313  * Based on:
35314  * Ext JS Library 1.1.1
35315  * Copyright(c) 2006-2007, Ext JS, LLC.
35316  *
35317  * Originally Released Under LGPL - original licence link has changed is not relivant.
35318  *
35319  * Fork - LGPL
35320  * <script type="text/javascript">
35321  */
35322 /**
35323  * @extends Roo.grid.AbstractSelectionModel
35324  * @class Roo.grid.RowSelectionModel
35325  * The default SelectionModel used by {@link Roo.grid.Grid}.
35326  * It supports multiple selections and keyboard selection/navigation. 
35327  * @constructor
35328  * @param {Object} config
35329  */
35330 Roo.grid.RowSelectionModel = function(config){
35331     Roo.apply(this, config);
35332     this.selections = new Roo.util.MixedCollection(false, function(o){
35333         return o.id;
35334     });
35335
35336     this.last = false;
35337     this.lastActive = false;
35338
35339     this.addEvents({
35340         /**
35341              * @event selectionchange
35342              * Fires when the selection changes
35343              * @param {SelectionModel} this
35344              */
35345             "selectionchange" : true,
35346         /**
35347              * @event afterselectionchange
35348              * Fires after the selection changes (eg. by key press or clicking)
35349              * @param {SelectionModel} this
35350              */
35351             "afterselectionchange" : true,
35352         /**
35353              * @event beforerowselect
35354              * Fires when a row is selected being selected, return false to cancel.
35355              * @param {SelectionModel} this
35356              * @param {Number} rowIndex The selected index
35357              * @param {Boolean} keepExisting False if other selections will be cleared
35358              */
35359             "beforerowselect" : true,
35360         /**
35361              * @event rowselect
35362              * Fires when a row is selected.
35363              * @param {SelectionModel} this
35364              * @param {Number} rowIndex The selected index
35365              * @param {Roo.data.Record} r The record
35366              */
35367             "rowselect" : true,
35368         /**
35369              * @event rowdeselect
35370              * Fires when a row is deselected.
35371              * @param {SelectionModel} this
35372              * @param {Number} rowIndex The selected index
35373              */
35374         "rowdeselect" : true
35375     });
35376     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
35377     this.locked = false;
35378 };
35379
35380 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
35381     /**
35382      * @cfg {Boolean} singleSelect
35383      * True to allow selection of only one row at a time (defaults to false)
35384      */
35385     singleSelect : false,
35386
35387     // private
35388     initEvents : function(){
35389
35390         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
35391             this.grid.on("mousedown", this.handleMouseDown, this);
35392         }else{ // allow click to work like normal
35393             this.grid.on("rowclick", this.handleDragableRowClick, this);
35394         }
35395
35396         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
35397             "up" : function(e){
35398                 if(!e.shiftKey){
35399                     this.selectPrevious(e.shiftKey);
35400                 }else if(this.last !== false && this.lastActive !== false){
35401                     var last = this.last;
35402                     this.selectRange(this.last,  this.lastActive-1);
35403                     this.grid.getView().focusRow(this.lastActive);
35404                     if(last !== false){
35405                         this.last = last;
35406                     }
35407                 }else{
35408                     this.selectFirstRow();
35409                 }
35410                 this.fireEvent("afterselectionchange", this);
35411             },
35412             "down" : function(e){
35413                 if(!e.shiftKey){
35414                     this.selectNext(e.shiftKey);
35415                 }else if(this.last !== false && this.lastActive !== false){
35416                     var last = this.last;
35417                     this.selectRange(this.last,  this.lastActive+1);
35418                     this.grid.getView().focusRow(this.lastActive);
35419                     if(last !== false){
35420                         this.last = last;
35421                     }
35422                 }else{
35423                     this.selectFirstRow();
35424                 }
35425                 this.fireEvent("afterselectionchange", this);
35426             },
35427             scope: this
35428         });
35429
35430         var view = this.grid.view;
35431         view.on("refresh", this.onRefresh, this);
35432         view.on("rowupdated", this.onRowUpdated, this);
35433         view.on("rowremoved", this.onRemove, this);
35434     },
35435
35436     // private
35437     onRefresh : function(){
35438         var ds = this.grid.dataSource, i, v = this.grid.view;
35439         var s = this.selections;
35440         s.each(function(r){
35441             if((i = ds.indexOfId(r.id)) != -1){
35442                 v.onRowSelect(i);
35443                 s.add(ds.getAt(i)); // updating the selection relate data
35444             }else{
35445                 s.remove(r);
35446             }
35447         });
35448     },
35449
35450     // private
35451     onRemove : function(v, index, r){
35452         this.selections.remove(r);
35453     },
35454
35455     // private
35456     onRowUpdated : function(v, index, r){
35457         if(this.isSelected(r)){
35458             v.onRowSelect(index);
35459         }
35460     },
35461
35462     /**
35463      * Select records.
35464      * @param {Array} records The records to select
35465      * @param {Boolean} keepExisting (optional) True to keep existing selections
35466      */
35467     selectRecords : function(records, keepExisting){
35468         if(!keepExisting){
35469             this.clearSelections();
35470         }
35471         var ds = this.grid.dataSource;
35472         for(var i = 0, len = records.length; i < len; i++){
35473             this.selectRow(ds.indexOf(records[i]), true);
35474         }
35475     },
35476
35477     /**
35478      * Gets the number of selected rows.
35479      * @return {Number}
35480      */
35481     getCount : function(){
35482         return this.selections.length;
35483     },
35484
35485     /**
35486      * Selects the first row in the grid.
35487      */
35488     selectFirstRow : function(){
35489         this.selectRow(0);
35490     },
35491
35492     /**
35493      * Select the last row.
35494      * @param {Boolean} keepExisting (optional) True to keep existing selections
35495      */
35496     selectLastRow : function(keepExisting){
35497         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
35498     },
35499
35500     /**
35501      * Selects the row immediately following the last selected row.
35502      * @param {Boolean} keepExisting (optional) True to keep existing selections
35503      */
35504     selectNext : function(keepExisting){
35505         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
35506             this.selectRow(this.last+1, keepExisting);
35507             this.grid.getView().focusRow(this.last);
35508         }
35509     },
35510
35511     /**
35512      * Selects the row that precedes the last selected row.
35513      * @param {Boolean} keepExisting (optional) True to keep existing selections
35514      */
35515     selectPrevious : function(keepExisting){
35516         if(this.last){
35517             this.selectRow(this.last-1, keepExisting);
35518             this.grid.getView().focusRow(this.last);
35519         }
35520     },
35521
35522     /**
35523      * Returns the selected records
35524      * @return {Array} Array of selected records
35525      */
35526     getSelections : function(){
35527         return [].concat(this.selections.items);
35528     },
35529
35530     /**
35531      * Returns the first selected record.
35532      * @return {Record}
35533      */
35534     getSelected : function(){
35535         return this.selections.itemAt(0);
35536     },
35537
35538
35539     /**
35540      * Clears all selections.
35541      */
35542     clearSelections : function(fast){
35543         if(this.locked) {
35544             return;
35545         }
35546         if(fast !== true){
35547             var ds = this.grid.dataSource;
35548             var s = this.selections;
35549             s.each(function(r){
35550                 this.deselectRow(ds.indexOfId(r.id));
35551             }, this);
35552             s.clear();
35553         }else{
35554             this.selections.clear();
35555         }
35556         this.last = false;
35557     },
35558
35559
35560     /**
35561      * Selects all rows.
35562      */
35563     selectAll : function(){
35564         if(this.locked) {
35565             return;
35566         }
35567         this.selections.clear();
35568         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
35569             this.selectRow(i, true);
35570         }
35571     },
35572
35573     /**
35574      * Returns True if there is a selection.
35575      * @return {Boolean}
35576      */
35577     hasSelection : function(){
35578         return this.selections.length > 0;
35579     },
35580
35581     /**
35582      * Returns True if the specified row is selected.
35583      * @param {Number/Record} record The record or index of the record to check
35584      * @return {Boolean}
35585      */
35586     isSelected : function(index){
35587         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
35588         return (r && this.selections.key(r.id) ? true : false);
35589     },
35590
35591     /**
35592      * Returns True if the specified record id is selected.
35593      * @param {String} id The id of record to check
35594      * @return {Boolean}
35595      */
35596     isIdSelected : function(id){
35597         return (this.selections.key(id) ? true : false);
35598     },
35599
35600     // private
35601     handleMouseDown : function(e, t){
35602         var view = this.grid.getView(), rowIndex;
35603         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
35604             return;
35605         };
35606         if(e.shiftKey && this.last !== false){
35607             var last = this.last;
35608             this.selectRange(last, rowIndex, e.ctrlKey);
35609             this.last = last; // reset the last
35610             view.focusRow(rowIndex);
35611         }else{
35612             var isSelected = this.isSelected(rowIndex);
35613             if(e.button !== 0 && isSelected){
35614                 view.focusRow(rowIndex);
35615             }else if(e.ctrlKey && isSelected){
35616                 this.deselectRow(rowIndex);
35617             }else if(!isSelected){
35618                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
35619                 view.focusRow(rowIndex);
35620             }
35621         }
35622         this.fireEvent("afterselectionchange", this);
35623     },
35624     // private
35625     handleDragableRowClick :  function(grid, rowIndex, e) 
35626     {
35627         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
35628             this.selectRow(rowIndex, false);
35629             grid.view.focusRow(rowIndex);
35630              this.fireEvent("afterselectionchange", this);
35631         }
35632     },
35633     
35634     /**
35635      * Selects multiple rows.
35636      * @param {Array} rows Array of the indexes of the row to select
35637      * @param {Boolean} keepExisting (optional) True to keep existing selections
35638      */
35639     selectRows : function(rows, keepExisting){
35640         if(!keepExisting){
35641             this.clearSelections();
35642         }
35643         for(var i = 0, len = rows.length; i < len; i++){
35644             this.selectRow(rows[i], true);
35645         }
35646     },
35647
35648     /**
35649      * Selects a range of rows. All rows in between startRow and endRow are also selected.
35650      * @param {Number} startRow The index of the first row in the range
35651      * @param {Number} endRow The index of the last row in the range
35652      * @param {Boolean} keepExisting (optional) True to retain existing selections
35653      */
35654     selectRange : function(startRow, endRow, keepExisting){
35655         if(this.locked) {
35656             return;
35657         }
35658         if(!keepExisting){
35659             this.clearSelections();
35660         }
35661         if(startRow <= endRow){
35662             for(var i = startRow; i <= endRow; i++){
35663                 this.selectRow(i, true);
35664             }
35665         }else{
35666             for(var i = startRow; i >= endRow; i--){
35667                 this.selectRow(i, true);
35668             }
35669         }
35670     },
35671
35672     /**
35673      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
35674      * @param {Number} startRow The index of the first row in the range
35675      * @param {Number} endRow The index of the last row in the range
35676      */
35677     deselectRange : function(startRow, endRow, preventViewNotify){
35678         if(this.locked) {
35679             return;
35680         }
35681         for(var i = startRow; i <= endRow; i++){
35682             this.deselectRow(i, preventViewNotify);
35683         }
35684     },
35685
35686     /**
35687      * Selects a row.
35688      * @param {Number} row The index of the row to select
35689      * @param {Boolean} keepExisting (optional) True to keep existing selections
35690      */
35691     selectRow : function(index, keepExisting, preventViewNotify){
35692         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
35693             return;
35694         }
35695         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
35696             if(!keepExisting || this.singleSelect){
35697                 this.clearSelections();
35698             }
35699             var r = this.grid.dataSource.getAt(index);
35700             this.selections.add(r);
35701             this.last = this.lastActive = index;
35702             if(!preventViewNotify){
35703                 this.grid.getView().onRowSelect(index);
35704             }
35705             this.fireEvent("rowselect", this, index, r);
35706             this.fireEvent("selectionchange", this);
35707         }
35708     },
35709
35710     /**
35711      * Deselects a row.
35712      * @param {Number} row The index of the row to deselect
35713      */
35714     deselectRow : function(index, preventViewNotify){
35715         if(this.locked) {
35716             return;
35717         }
35718         if(this.last == index){
35719             this.last = false;
35720         }
35721         if(this.lastActive == index){
35722             this.lastActive = false;
35723         }
35724         var r = this.grid.dataSource.getAt(index);
35725         this.selections.remove(r);
35726         if(!preventViewNotify){
35727             this.grid.getView().onRowDeselect(index);
35728         }
35729         this.fireEvent("rowdeselect", this, index);
35730         this.fireEvent("selectionchange", this);
35731     },
35732
35733     // private
35734     restoreLast : function(){
35735         if(this._last){
35736             this.last = this._last;
35737         }
35738     },
35739
35740     // private
35741     acceptsNav : function(row, col, cm){
35742         return !cm.isHidden(col) && cm.isCellEditable(col, row);
35743     },
35744
35745     // private
35746     onEditorKey : function(field, e){
35747         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
35748         if(k == e.TAB){
35749             e.stopEvent();
35750             ed.completeEdit();
35751             if(e.shiftKey){
35752                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
35753             }else{
35754                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
35755             }
35756         }else if(k == e.ENTER && !e.ctrlKey){
35757             e.stopEvent();
35758             ed.completeEdit();
35759             if(e.shiftKey){
35760                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
35761             }else{
35762                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
35763             }
35764         }else if(k == e.ESC){
35765             ed.cancelEdit();
35766         }
35767         if(newCell){
35768             g.startEditing(newCell[0], newCell[1]);
35769         }
35770     }
35771 });/*
35772  * Based on:
35773  * Ext JS Library 1.1.1
35774  * Copyright(c) 2006-2007, Ext JS, LLC.
35775  *
35776  * Originally Released Under LGPL - original licence link has changed is not relivant.
35777  *
35778  * Fork - LGPL
35779  * <script type="text/javascript">
35780  */
35781 /**
35782  * @class Roo.grid.CellSelectionModel
35783  * @extends Roo.grid.AbstractSelectionModel
35784  * This class provides the basic implementation for cell selection in a grid.
35785  * @constructor
35786  * @param {Object} config The object containing the configuration of this model.
35787  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
35788  */
35789 Roo.grid.CellSelectionModel = function(config){
35790     Roo.apply(this, config);
35791
35792     this.selection = null;
35793
35794     this.addEvents({
35795         /**
35796              * @event beforerowselect
35797              * Fires before a cell is selected.
35798              * @param {SelectionModel} this
35799              * @param {Number} rowIndex The selected row index
35800              * @param {Number} colIndex The selected cell index
35801              */
35802             "beforecellselect" : true,
35803         /**
35804              * @event cellselect
35805              * Fires when a cell is selected.
35806              * @param {SelectionModel} this
35807              * @param {Number} rowIndex The selected row index
35808              * @param {Number} colIndex The selected cell index
35809              */
35810             "cellselect" : true,
35811         /**
35812              * @event selectionchange
35813              * Fires when the active selection changes.
35814              * @param {SelectionModel} this
35815              * @param {Object} selection null for no selection or an object (o) with two properties
35816                 <ul>
35817                 <li>o.record: the record object for the row the selection is in</li>
35818                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
35819                 </ul>
35820              */
35821             "selectionchange" : true,
35822         /**
35823              * @event tabend
35824              * Fires when the tab (or enter) was pressed on the last editable cell
35825              * You can use this to trigger add new row.
35826              * @param {SelectionModel} this
35827              */
35828             "tabend" : true,
35829          /**
35830              * @event beforeeditnext
35831              * Fires before the next editable sell is made active
35832              * You can use this to skip to another cell or fire the tabend
35833              *    if you set cell to false
35834              * @param {Object} eventdata object : { cell : [ row, col ] } 
35835              */
35836             "beforeeditnext" : true
35837     });
35838     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
35839 };
35840
35841 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
35842     
35843     enter_is_tab: false,
35844
35845     /** @ignore */
35846     initEvents : function(){
35847         this.grid.on("mousedown", this.handleMouseDown, this);
35848         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
35849         var view = this.grid.view;
35850         view.on("refresh", this.onViewChange, this);
35851         view.on("rowupdated", this.onRowUpdated, this);
35852         view.on("beforerowremoved", this.clearSelections, this);
35853         view.on("beforerowsinserted", this.clearSelections, this);
35854         if(this.grid.isEditor){
35855             this.grid.on("beforeedit", this.beforeEdit,  this);
35856         }
35857     },
35858
35859         //private
35860     beforeEdit : function(e){
35861         this.select(e.row, e.column, false, true, e.record);
35862     },
35863
35864         //private
35865     onRowUpdated : function(v, index, r){
35866         if(this.selection && this.selection.record == r){
35867             v.onCellSelect(index, this.selection.cell[1]);
35868         }
35869     },
35870
35871         //private
35872     onViewChange : function(){
35873         this.clearSelections(true);
35874     },
35875
35876         /**
35877          * Returns the currently selected cell,.
35878          * @return {Array} The selected cell (row, column) or null if none selected.
35879          */
35880     getSelectedCell : function(){
35881         return this.selection ? this.selection.cell : null;
35882     },
35883
35884     /**
35885      * Clears all selections.
35886      * @param {Boolean} true to prevent the gridview from being notified about the change.
35887      */
35888     clearSelections : function(preventNotify){
35889         var s = this.selection;
35890         if(s){
35891             if(preventNotify !== true){
35892                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
35893             }
35894             this.selection = null;
35895             this.fireEvent("selectionchange", this, null);
35896         }
35897     },
35898
35899     /**
35900      * Returns true if there is a selection.
35901      * @return {Boolean}
35902      */
35903     hasSelection : function(){
35904         return this.selection ? true : false;
35905     },
35906
35907     /** @ignore */
35908     handleMouseDown : function(e, t){
35909         var v = this.grid.getView();
35910         if(this.isLocked()){
35911             return;
35912         };
35913         var row = v.findRowIndex(t);
35914         var cell = v.findCellIndex(t);
35915         if(row !== false && cell !== false){
35916             this.select(row, cell);
35917         }
35918     },
35919
35920     /**
35921      * Selects a cell.
35922      * @param {Number} rowIndex
35923      * @param {Number} collIndex
35924      */
35925     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
35926         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
35927             this.clearSelections();
35928             r = r || this.grid.dataSource.getAt(rowIndex);
35929             this.selection = {
35930                 record : r,
35931                 cell : [rowIndex, colIndex]
35932             };
35933             if(!preventViewNotify){
35934                 var v = this.grid.getView();
35935                 v.onCellSelect(rowIndex, colIndex);
35936                 if(preventFocus !== true){
35937                     v.focusCell(rowIndex, colIndex);
35938                 }
35939             }
35940             this.fireEvent("cellselect", this, rowIndex, colIndex);
35941             this.fireEvent("selectionchange", this, this.selection);
35942         }
35943     },
35944
35945         //private
35946     isSelectable : function(rowIndex, colIndex, cm){
35947         return !cm.isHidden(colIndex);
35948     },
35949
35950     /** @ignore */
35951     handleKeyDown : function(e){
35952         //Roo.log('Cell Sel Model handleKeyDown');
35953         if(!e.isNavKeyPress()){
35954             return;
35955         }
35956         var g = this.grid, s = this.selection;
35957         if(!s){
35958             e.stopEvent();
35959             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
35960             if(cell){
35961                 this.select(cell[0], cell[1]);
35962             }
35963             return;
35964         }
35965         var sm = this;
35966         var walk = function(row, col, step){
35967             return g.walkCells(row, col, step, sm.isSelectable,  sm);
35968         };
35969         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
35970         var newCell;
35971
35972       
35973
35974         switch(k){
35975             case e.TAB:
35976                 // handled by onEditorKey
35977                 if (g.isEditor && g.editing) {
35978                     return;
35979                 }
35980                 if(e.shiftKey) {
35981                     newCell = walk(r, c-1, -1);
35982                 } else {
35983                     newCell = walk(r, c+1, 1);
35984                 }
35985                 break;
35986             
35987             case e.DOWN:
35988                newCell = walk(r+1, c, 1);
35989                 break;
35990             
35991             case e.UP:
35992                 newCell = walk(r-1, c, -1);
35993                 break;
35994             
35995             case e.RIGHT:
35996                 newCell = walk(r, c+1, 1);
35997                 break;
35998             
35999             case e.LEFT:
36000                 newCell = walk(r, c-1, -1);
36001                 break;
36002             
36003             case e.ENTER:
36004                 
36005                 if(g.isEditor && !g.editing){
36006                    g.startEditing(r, c);
36007                    e.stopEvent();
36008                    return;
36009                 }
36010                 
36011                 
36012              break;
36013         };
36014         if(newCell){
36015             this.select(newCell[0], newCell[1]);
36016             e.stopEvent();
36017             
36018         }
36019     },
36020
36021     acceptsNav : function(row, col, cm){
36022         return !cm.isHidden(col) && cm.isCellEditable(col, row);
36023     },
36024     /**
36025      * Selects a cell.
36026      * @param {Number} field (not used) - as it's normally used as a listener
36027      * @param {Number} e - event - fake it by using
36028      *
36029      * var e = Roo.EventObjectImpl.prototype;
36030      * e.keyCode = e.TAB
36031      *
36032      * 
36033      */
36034     onEditorKey : function(field, e){
36035         
36036         var k = e.getKey(),
36037             newCell,
36038             g = this.grid,
36039             ed = g.activeEditor,
36040             forward = false;
36041         ///Roo.log('onEditorKey' + k);
36042         
36043         
36044         if (this.enter_is_tab && k == e.ENTER) {
36045             k = e.TAB;
36046         }
36047         
36048         if(k == e.TAB){
36049             if(e.shiftKey){
36050                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36051             }else{
36052                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36053                 forward = true;
36054             }
36055             
36056             e.stopEvent();
36057             
36058         } else if(k == e.ENTER &&  !e.ctrlKey){
36059             ed.completeEdit();
36060             e.stopEvent();
36061             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36062         
36063                 } else if(k == e.ESC){
36064             ed.cancelEdit();
36065         }
36066                 
36067         if (newCell) {
36068             var ecall = { cell : newCell, forward : forward };
36069             this.fireEvent('beforeeditnext', ecall );
36070             newCell = ecall.cell;
36071                         forward = ecall.forward;
36072         }
36073                 
36074         if(newCell){
36075             //Roo.log('next cell after edit');
36076             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
36077         } else if (forward) {
36078             // tabbed past last
36079             this.fireEvent.defer(100, this, ['tabend',this]);
36080         }
36081     }
36082 });/*
36083  * Based on:
36084  * Ext JS Library 1.1.1
36085  * Copyright(c) 2006-2007, Ext JS, LLC.
36086  *
36087  * Originally Released Under LGPL - original licence link has changed is not relivant.
36088  *
36089  * Fork - LGPL
36090  * <script type="text/javascript">
36091  */
36092  
36093 /**
36094  * @class Roo.grid.EditorGrid
36095  * @extends Roo.grid.Grid
36096  * Class for creating and editable grid.
36097  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
36098  * The container MUST have some type of size defined for the grid to fill. The container will be 
36099  * automatically set to position relative if it isn't already.
36100  * @param {Object} dataSource The data model to bind to
36101  * @param {Object} colModel The column model with info about this grid's columns
36102  */
36103 Roo.grid.EditorGrid = function(container, config){
36104     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
36105     this.getGridEl().addClass("xedit-grid");
36106
36107     if(!this.selModel){
36108         this.selModel = new Roo.grid.CellSelectionModel();
36109     }
36110
36111     this.activeEditor = null;
36112
36113         this.addEvents({
36114             /**
36115              * @event beforeedit
36116              * Fires before cell editing is triggered. The edit event object has the following properties <br />
36117              * <ul style="padding:5px;padding-left:16px;">
36118              * <li>grid - This grid</li>
36119              * <li>record - The record being edited</li>
36120              * <li>field - The field name being edited</li>
36121              * <li>value - The value for the field being edited.</li>
36122              * <li>row - The grid row index</li>
36123              * <li>column - The grid column index</li>
36124              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36125              * </ul>
36126              * @param {Object} e An edit event (see above for description)
36127              */
36128             "beforeedit" : true,
36129             /**
36130              * @event afteredit
36131              * Fires after a cell is edited. <br />
36132              * <ul style="padding:5px;padding-left:16px;">
36133              * <li>grid - This grid</li>
36134              * <li>record - The record being edited</li>
36135              * <li>field - The field name being edited</li>
36136              * <li>value - The value being set</li>
36137              * <li>originalValue - The original value for the field, before the edit.</li>
36138              * <li>row - The grid row index</li>
36139              * <li>column - The grid column index</li>
36140              * </ul>
36141              * @param {Object} e An edit event (see above for description)
36142              */
36143             "afteredit" : true,
36144             /**
36145              * @event validateedit
36146              * Fires after a cell is edited, but before the value is set in the record. 
36147          * You can use this to modify the value being set in the field, Return false
36148              * to cancel the change. The edit event object has the following properties <br />
36149              * <ul style="padding:5px;padding-left:16px;">
36150          * <li>editor - This editor</li>
36151              * <li>grid - This grid</li>
36152              * <li>record - The record being edited</li>
36153              * <li>field - The field name being edited</li>
36154              * <li>value - The value being set</li>
36155              * <li>originalValue - The original value for the field, before the edit.</li>
36156              * <li>row - The grid row index</li>
36157              * <li>column - The grid column index</li>
36158              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36159              * </ul>
36160              * @param {Object} e An edit event (see above for description)
36161              */
36162             "validateedit" : true
36163         });
36164     this.on("bodyscroll", this.stopEditing,  this);
36165     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
36166 };
36167
36168 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
36169     /**
36170      * @cfg {Number} clicksToEdit
36171      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
36172      */
36173     clicksToEdit: 2,
36174
36175     // private
36176     isEditor : true,
36177     // private
36178     trackMouseOver: false, // causes very odd FF errors
36179
36180     onCellDblClick : function(g, row, col){
36181         this.startEditing(row, col);
36182     },
36183
36184     onEditComplete : function(ed, value, startValue){
36185         this.editing = false;
36186         this.activeEditor = null;
36187         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
36188         var r = ed.record;
36189         var field = this.colModel.getDataIndex(ed.col);
36190         var e = {
36191             grid: this,
36192             record: r,
36193             field: field,
36194             originalValue: startValue,
36195             value: value,
36196             row: ed.row,
36197             column: ed.col,
36198             cancel:false,
36199             editor: ed
36200         };
36201         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
36202         cell.show();
36203           
36204         if(String(value) !== String(startValue)){
36205             
36206             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
36207                 r.set(field, e.value);
36208                 // if we are dealing with a combo box..
36209                 // then we also set the 'name' colum to be the displayField
36210                 if (ed.field.displayField && ed.field.name) {
36211                     r.set(ed.field.name, ed.field.el.dom.value);
36212                 }
36213                 
36214                 delete e.cancel; //?? why!!!
36215                 this.fireEvent("afteredit", e);
36216             }
36217         } else {
36218             this.fireEvent("afteredit", e); // always fire it!
36219         }
36220         this.view.focusCell(ed.row, ed.col);
36221     },
36222
36223     /**
36224      * Starts editing the specified for the specified row/column
36225      * @param {Number} rowIndex
36226      * @param {Number} colIndex
36227      */
36228     startEditing : function(row, col){
36229         this.stopEditing();
36230         if(this.colModel.isCellEditable(col, row)){
36231             this.view.ensureVisible(row, col, true);
36232           
36233             var r = this.dataSource.getAt(row);
36234             var field = this.colModel.getDataIndex(col);
36235             var cell = Roo.get(this.view.getCell(row,col));
36236             var e = {
36237                 grid: this,
36238                 record: r,
36239                 field: field,
36240                 value: r.data[field],
36241                 row: row,
36242                 column: col,
36243                 cancel:false 
36244             };
36245             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
36246                 this.editing = true;
36247                 var ed = this.colModel.getCellEditor(col, row);
36248                 
36249                 if (!ed) {
36250                     return;
36251                 }
36252                 if(!ed.rendered){
36253                     ed.render(ed.parentEl || document.body);
36254                 }
36255                 ed.field.reset();
36256                
36257                 cell.hide();
36258                 
36259                 (function(){ // complex but required for focus issues in safari, ie and opera
36260                     ed.row = row;
36261                     ed.col = col;
36262                     ed.record = r;
36263                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
36264                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
36265                     this.activeEditor = ed;
36266                     var v = r.data[field];
36267                     ed.startEdit(this.view.getCell(row, col), v);
36268                     // combo's with 'displayField and name set
36269                     if (ed.field.displayField && ed.field.name) {
36270                         ed.field.el.dom.value = r.data[ed.field.name];
36271                     }
36272                     
36273                     
36274                 }).defer(50, this);
36275             }
36276         }
36277     },
36278         
36279     /**
36280      * Stops any active editing
36281      */
36282     stopEditing : function(){
36283         if(this.activeEditor){
36284             this.activeEditor.completeEdit();
36285         }
36286         this.activeEditor = null;
36287     },
36288         
36289          /**
36290      * Called to get grid's drag proxy text, by default returns this.ddText.
36291      * @return {String}
36292      */
36293     getDragDropText : function(){
36294         var count = this.selModel.getSelectedCell() ? 1 : 0;
36295         return String.format(this.ddText, count, count == 1 ? '' : 's');
36296     }
36297         
36298 });/*
36299  * Based on:
36300  * Ext JS Library 1.1.1
36301  * Copyright(c) 2006-2007, Ext JS, LLC.
36302  *
36303  * Originally Released Under LGPL - original licence link has changed is not relivant.
36304  *
36305  * Fork - LGPL
36306  * <script type="text/javascript">
36307  */
36308
36309 // private - not really -- you end up using it !
36310 // This is a support class used internally by the Grid components
36311
36312 /**
36313  * @class Roo.grid.GridEditor
36314  * @extends Roo.Editor
36315  * Class for creating and editable grid elements.
36316  * @param {Object} config any settings (must include field)
36317  */
36318 Roo.grid.GridEditor = function(field, config){
36319     if (!config && field.field) {
36320         config = field;
36321         field = Roo.factory(config.field, Roo.form);
36322     }
36323     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
36324     field.monitorTab = false;
36325 };
36326
36327 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
36328     
36329     /**
36330      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
36331      */
36332     
36333     alignment: "tl-tl",
36334     autoSize: "width",
36335     hideEl : false,
36336     cls: "x-small-editor x-grid-editor",
36337     shim:false,
36338     shadow:"frame"
36339 });/*
36340  * Based on:
36341  * Ext JS Library 1.1.1
36342  * Copyright(c) 2006-2007, Ext JS, LLC.
36343  *
36344  * Originally Released Under LGPL - original licence link has changed is not relivant.
36345  *
36346  * Fork - LGPL
36347  * <script type="text/javascript">
36348  */
36349   
36350
36351   
36352 Roo.grid.PropertyRecord = Roo.data.Record.create([
36353     {name:'name',type:'string'},  'value'
36354 ]);
36355
36356
36357 Roo.grid.PropertyStore = function(grid, source){
36358     this.grid = grid;
36359     this.store = new Roo.data.Store({
36360         recordType : Roo.grid.PropertyRecord
36361     });
36362     this.store.on('update', this.onUpdate,  this);
36363     if(source){
36364         this.setSource(source);
36365     }
36366     Roo.grid.PropertyStore.superclass.constructor.call(this);
36367 };
36368
36369
36370
36371 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
36372     setSource : function(o){
36373         this.source = o;
36374         this.store.removeAll();
36375         var data = [];
36376         for(var k in o){
36377             if(this.isEditableValue(o[k])){
36378                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
36379             }
36380         }
36381         this.store.loadRecords({records: data}, {}, true);
36382     },
36383
36384     onUpdate : function(ds, record, type){
36385         if(type == Roo.data.Record.EDIT){
36386             var v = record.data['value'];
36387             var oldValue = record.modified['value'];
36388             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
36389                 this.source[record.id] = v;
36390                 record.commit();
36391                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
36392             }else{
36393                 record.reject();
36394             }
36395         }
36396     },
36397
36398     getProperty : function(row){
36399        return this.store.getAt(row);
36400     },
36401
36402     isEditableValue: function(val){
36403         if(val && val instanceof Date){
36404             return true;
36405         }else if(typeof val == 'object' || typeof val == 'function'){
36406             return false;
36407         }
36408         return true;
36409     },
36410
36411     setValue : function(prop, value){
36412         this.source[prop] = value;
36413         this.store.getById(prop).set('value', value);
36414     },
36415
36416     getSource : function(){
36417         return this.source;
36418     }
36419 });
36420
36421 Roo.grid.PropertyColumnModel = function(grid, store){
36422     this.grid = grid;
36423     var g = Roo.grid;
36424     g.PropertyColumnModel.superclass.constructor.call(this, [
36425         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
36426         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
36427     ]);
36428     this.store = store;
36429     this.bselect = Roo.DomHelper.append(document.body, {
36430         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
36431             {tag: 'option', value: 'true', html: 'true'},
36432             {tag: 'option', value: 'false', html: 'false'}
36433         ]
36434     });
36435     Roo.id(this.bselect);
36436     var f = Roo.form;
36437     this.editors = {
36438         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
36439         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
36440         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
36441         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
36442         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
36443     };
36444     this.renderCellDelegate = this.renderCell.createDelegate(this);
36445     this.renderPropDelegate = this.renderProp.createDelegate(this);
36446 };
36447
36448 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
36449     
36450     
36451     nameText : 'Name',
36452     valueText : 'Value',
36453     
36454     dateFormat : 'm/j/Y',
36455     
36456     
36457     renderDate : function(dateVal){
36458         return dateVal.dateFormat(this.dateFormat);
36459     },
36460
36461     renderBool : function(bVal){
36462         return bVal ? 'true' : 'false';
36463     },
36464
36465     isCellEditable : function(colIndex, rowIndex){
36466         return colIndex == 1;
36467     },
36468
36469     getRenderer : function(col){
36470         return col == 1 ?
36471             this.renderCellDelegate : this.renderPropDelegate;
36472     },
36473
36474     renderProp : function(v){
36475         return this.getPropertyName(v);
36476     },
36477
36478     renderCell : function(val){
36479         var rv = val;
36480         if(val instanceof Date){
36481             rv = this.renderDate(val);
36482         }else if(typeof val == 'boolean'){
36483             rv = this.renderBool(val);
36484         }
36485         return Roo.util.Format.htmlEncode(rv);
36486     },
36487
36488     getPropertyName : function(name){
36489         var pn = this.grid.propertyNames;
36490         return pn && pn[name] ? pn[name] : name;
36491     },
36492
36493     getCellEditor : function(colIndex, rowIndex){
36494         var p = this.store.getProperty(rowIndex);
36495         var n = p.data['name'], val = p.data['value'];
36496         
36497         if(typeof(this.grid.customEditors[n]) == 'string'){
36498             return this.editors[this.grid.customEditors[n]];
36499         }
36500         if(typeof(this.grid.customEditors[n]) != 'undefined'){
36501             return this.grid.customEditors[n];
36502         }
36503         if(val instanceof Date){
36504             return this.editors['date'];
36505         }else if(typeof val == 'number'){
36506             return this.editors['number'];
36507         }else if(typeof val == 'boolean'){
36508             return this.editors['boolean'];
36509         }else{
36510             return this.editors['string'];
36511         }
36512     }
36513 });
36514
36515 /**
36516  * @class Roo.grid.PropertyGrid
36517  * @extends Roo.grid.EditorGrid
36518  * This class represents the  interface of a component based property grid control.
36519  * <br><br>Usage:<pre><code>
36520  var grid = new Roo.grid.PropertyGrid("my-container-id", {
36521       
36522  });
36523  // set any options
36524  grid.render();
36525  * </code></pre>
36526   
36527  * @constructor
36528  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
36529  * The container MUST have some type of size defined for the grid to fill. The container will be
36530  * automatically set to position relative if it isn't already.
36531  * @param {Object} config A config object that sets properties on this grid.
36532  */
36533 Roo.grid.PropertyGrid = function(container, config){
36534     config = config || {};
36535     var store = new Roo.grid.PropertyStore(this);
36536     this.store = store;
36537     var cm = new Roo.grid.PropertyColumnModel(this, store);
36538     store.store.sort('name', 'ASC');
36539     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
36540         ds: store.store,
36541         cm: cm,
36542         enableColLock:false,
36543         enableColumnMove:false,
36544         stripeRows:false,
36545         trackMouseOver: false,
36546         clicksToEdit:1
36547     }, config));
36548     this.getGridEl().addClass('x-props-grid');
36549     this.lastEditRow = null;
36550     this.on('columnresize', this.onColumnResize, this);
36551     this.addEvents({
36552          /**
36553              * @event beforepropertychange
36554              * Fires before a property changes (return false to stop?)
36555              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
36556              * @param {String} id Record Id
36557              * @param {String} newval New Value
36558          * @param {String} oldval Old Value
36559              */
36560         "beforepropertychange": true,
36561         /**
36562              * @event propertychange
36563              * Fires after a property changes
36564              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
36565              * @param {String} id Record Id
36566              * @param {String} newval New Value
36567          * @param {String} oldval Old Value
36568              */
36569         "propertychange": true
36570     });
36571     this.customEditors = this.customEditors || {};
36572 };
36573 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
36574     
36575      /**
36576      * @cfg {Object} customEditors map of colnames=> custom editors.
36577      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
36578      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
36579      * false disables editing of the field.
36580          */
36581     
36582       /**
36583      * @cfg {Object} propertyNames map of property Names to their displayed value
36584          */
36585     
36586     render : function(){
36587         Roo.grid.PropertyGrid.superclass.render.call(this);
36588         this.autoSize.defer(100, this);
36589     },
36590
36591     autoSize : function(){
36592         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
36593         if(this.view){
36594             this.view.fitColumns();
36595         }
36596     },
36597
36598     onColumnResize : function(){
36599         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
36600         this.autoSize();
36601     },
36602     /**
36603      * Sets the data for the Grid
36604      * accepts a Key => Value object of all the elements avaiable.
36605      * @param {Object} data  to appear in grid.
36606      */
36607     setSource : function(source){
36608         this.store.setSource(source);
36609         //this.autoSize();
36610     },
36611     /**
36612      * Gets all the data from the grid.
36613      * @return {Object} data  data stored in grid
36614      */
36615     getSource : function(){
36616         return this.store.getSource();
36617     }
36618 });/*
36619   
36620  * Licence LGPL
36621  
36622  */
36623  
36624 /**
36625  * @class Roo.grid.Calendar
36626  * @extends Roo.util.Grid
36627  * This class extends the Grid to provide a calendar widget
36628  * <br><br>Usage:<pre><code>
36629  var grid = new Roo.grid.Calendar("my-container-id", {
36630      ds: myDataStore,
36631      cm: myColModel,
36632      selModel: mySelectionModel,
36633      autoSizeColumns: true,
36634      monitorWindowResize: false,
36635      trackMouseOver: true
36636      eventstore : real data store..
36637  });
36638  // set any options
36639  grid.render();
36640   
36641   * @constructor
36642  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
36643  * The container MUST have some type of size defined for the grid to fill. The container will be
36644  * automatically set to position relative if it isn't already.
36645  * @param {Object} config A config object that sets properties on this grid.
36646  */
36647 Roo.grid.Calendar = function(container, config){
36648         // initialize the container
36649         this.container = Roo.get(container);
36650         this.container.update("");
36651         this.container.setStyle("overflow", "hidden");
36652     this.container.addClass('x-grid-container');
36653
36654     this.id = this.container.id;
36655
36656     Roo.apply(this, config);
36657     // check and correct shorthanded configs
36658     
36659     var rows = [];
36660     var d =1;
36661     for (var r = 0;r < 6;r++) {
36662         
36663         rows[r]=[];
36664         for (var c =0;c < 7;c++) {
36665             rows[r][c]= '';
36666         }
36667     }
36668     if (this.eventStore) {
36669         this.eventStore= Roo.factory(this.eventStore, Roo.data);
36670         this.eventStore.on('load',this.onLoad, this);
36671         this.eventStore.on('beforeload',this.clearEvents, this);
36672          
36673     }
36674     
36675     this.dataSource = new Roo.data.Store({
36676             proxy: new Roo.data.MemoryProxy(rows),
36677             reader: new Roo.data.ArrayReader({}, [
36678                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
36679     });
36680
36681     this.dataSource.load();
36682     this.ds = this.dataSource;
36683     this.ds.xmodule = this.xmodule || false;
36684     
36685     
36686     var cellRender = function(v,x,r)
36687     {
36688         return String.format(
36689             '<div class="fc-day  fc-widget-content"><div>' +
36690                 '<div class="fc-event-container"></div>' +
36691                 '<div class="fc-day-number">{0}</div>'+
36692                 
36693                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
36694             '</div></div>', v);
36695     
36696     }
36697     
36698     
36699     this.colModel = new Roo.grid.ColumnModel( [
36700         {
36701             xtype: 'ColumnModel',
36702             xns: Roo.grid,
36703             dataIndex : 'weekday0',
36704             header : 'Sunday',
36705             renderer : cellRender
36706         },
36707         {
36708             xtype: 'ColumnModel',
36709             xns: Roo.grid,
36710             dataIndex : 'weekday1',
36711             header : 'Monday',
36712             renderer : cellRender
36713         },
36714         {
36715             xtype: 'ColumnModel',
36716             xns: Roo.grid,
36717             dataIndex : 'weekday2',
36718             header : 'Tuesday',
36719             renderer : cellRender
36720         },
36721         {
36722             xtype: 'ColumnModel',
36723             xns: Roo.grid,
36724             dataIndex : 'weekday3',
36725             header : 'Wednesday',
36726             renderer : cellRender
36727         },
36728         {
36729             xtype: 'ColumnModel',
36730             xns: Roo.grid,
36731             dataIndex : 'weekday4',
36732             header : 'Thursday',
36733             renderer : cellRender
36734         },
36735         {
36736             xtype: 'ColumnModel',
36737             xns: Roo.grid,
36738             dataIndex : 'weekday5',
36739             header : 'Friday',
36740             renderer : cellRender
36741         },
36742         {
36743             xtype: 'ColumnModel',
36744             xns: Roo.grid,
36745             dataIndex : 'weekday6',
36746             header : 'Saturday',
36747             renderer : cellRender
36748         }
36749     ]);
36750     this.cm = this.colModel;
36751     this.cm.xmodule = this.xmodule || false;
36752  
36753         
36754           
36755     //this.selModel = new Roo.grid.CellSelectionModel();
36756     //this.sm = this.selModel;
36757     //this.selModel.init(this);
36758     
36759     
36760     if(this.width){
36761         this.container.setWidth(this.width);
36762     }
36763
36764     if(this.height){
36765         this.container.setHeight(this.height);
36766     }
36767     /** @private */
36768         this.addEvents({
36769         // raw events
36770         /**
36771          * @event click
36772          * The raw click event for the entire grid.
36773          * @param {Roo.EventObject} e
36774          */
36775         "click" : true,
36776         /**
36777          * @event dblclick
36778          * The raw dblclick event for the entire grid.
36779          * @param {Roo.EventObject} e
36780          */
36781         "dblclick" : true,
36782         /**
36783          * @event contextmenu
36784          * The raw contextmenu event for the entire grid.
36785          * @param {Roo.EventObject} e
36786          */
36787         "contextmenu" : true,
36788         /**
36789          * @event mousedown
36790          * The raw mousedown event for the entire grid.
36791          * @param {Roo.EventObject} e
36792          */
36793         "mousedown" : true,
36794         /**
36795          * @event mouseup
36796          * The raw mouseup event for the entire grid.
36797          * @param {Roo.EventObject} e
36798          */
36799         "mouseup" : true,
36800         /**
36801          * @event mouseover
36802          * The raw mouseover event for the entire grid.
36803          * @param {Roo.EventObject} e
36804          */
36805         "mouseover" : true,
36806         /**
36807          * @event mouseout
36808          * The raw mouseout event for the entire grid.
36809          * @param {Roo.EventObject} e
36810          */
36811         "mouseout" : true,
36812         /**
36813          * @event keypress
36814          * The raw keypress event for the entire grid.
36815          * @param {Roo.EventObject} e
36816          */
36817         "keypress" : true,
36818         /**
36819          * @event keydown
36820          * The raw keydown event for the entire grid.
36821          * @param {Roo.EventObject} e
36822          */
36823         "keydown" : true,
36824
36825         // custom events
36826
36827         /**
36828          * @event cellclick
36829          * Fires when a cell is clicked
36830          * @param {Grid} this
36831          * @param {Number} rowIndex
36832          * @param {Number} columnIndex
36833          * @param {Roo.EventObject} e
36834          */
36835         "cellclick" : true,
36836         /**
36837          * @event celldblclick
36838          * Fires when a cell is double clicked
36839          * @param {Grid} this
36840          * @param {Number} rowIndex
36841          * @param {Number} columnIndex
36842          * @param {Roo.EventObject} e
36843          */
36844         "celldblclick" : true,
36845         /**
36846          * @event rowclick
36847          * Fires when a row is clicked
36848          * @param {Grid} this
36849          * @param {Number} rowIndex
36850          * @param {Roo.EventObject} e
36851          */
36852         "rowclick" : true,
36853         /**
36854          * @event rowdblclick
36855          * Fires when a row is double clicked
36856          * @param {Grid} this
36857          * @param {Number} rowIndex
36858          * @param {Roo.EventObject} e
36859          */
36860         "rowdblclick" : true,
36861         /**
36862          * @event headerclick
36863          * Fires when a header is clicked
36864          * @param {Grid} this
36865          * @param {Number} columnIndex
36866          * @param {Roo.EventObject} e
36867          */
36868         "headerclick" : true,
36869         /**
36870          * @event headerdblclick
36871          * Fires when a header cell is double clicked
36872          * @param {Grid} this
36873          * @param {Number} columnIndex
36874          * @param {Roo.EventObject} e
36875          */
36876         "headerdblclick" : true,
36877         /**
36878          * @event rowcontextmenu
36879          * Fires when a row is right clicked
36880          * @param {Grid} this
36881          * @param {Number} rowIndex
36882          * @param {Roo.EventObject} e
36883          */
36884         "rowcontextmenu" : true,
36885         /**
36886          * @event cellcontextmenu
36887          * Fires when a cell is right clicked
36888          * @param {Grid} this
36889          * @param {Number} rowIndex
36890          * @param {Number} cellIndex
36891          * @param {Roo.EventObject} e
36892          */
36893          "cellcontextmenu" : true,
36894         /**
36895          * @event headercontextmenu
36896          * Fires when a header is right clicked
36897          * @param {Grid} this
36898          * @param {Number} columnIndex
36899          * @param {Roo.EventObject} e
36900          */
36901         "headercontextmenu" : true,
36902         /**
36903          * @event bodyscroll
36904          * Fires when the body element is scrolled
36905          * @param {Number} scrollLeft
36906          * @param {Number} scrollTop
36907          */
36908         "bodyscroll" : true,
36909         /**
36910          * @event columnresize
36911          * Fires when the user resizes a column
36912          * @param {Number} columnIndex
36913          * @param {Number} newSize
36914          */
36915         "columnresize" : true,
36916         /**
36917          * @event columnmove
36918          * Fires when the user moves a column
36919          * @param {Number} oldIndex
36920          * @param {Number} newIndex
36921          */
36922         "columnmove" : true,
36923         /**
36924          * @event startdrag
36925          * Fires when row(s) start being dragged
36926          * @param {Grid} this
36927          * @param {Roo.GridDD} dd The drag drop object
36928          * @param {event} e The raw browser event
36929          */
36930         "startdrag" : true,
36931         /**
36932          * @event enddrag
36933          * Fires when a drag operation is complete
36934          * @param {Grid} this
36935          * @param {Roo.GridDD} dd The drag drop object
36936          * @param {event} e The raw browser event
36937          */
36938         "enddrag" : true,
36939         /**
36940          * @event dragdrop
36941          * Fires when dragged row(s) are dropped on a valid DD target
36942          * @param {Grid} this
36943          * @param {Roo.GridDD} dd The drag drop object
36944          * @param {String} targetId The target drag drop object
36945          * @param {event} e The raw browser event
36946          */
36947         "dragdrop" : true,
36948         /**
36949          * @event dragover
36950          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
36951          * @param {Grid} this
36952          * @param {Roo.GridDD} dd The drag drop object
36953          * @param {String} targetId The target drag drop object
36954          * @param {event} e The raw browser event
36955          */
36956         "dragover" : true,
36957         /**
36958          * @event dragenter
36959          *  Fires when the dragged row(s) first cross another DD target while being dragged
36960          * @param {Grid} this
36961          * @param {Roo.GridDD} dd The drag drop object
36962          * @param {String} targetId The target drag drop object
36963          * @param {event} e The raw browser event
36964          */
36965         "dragenter" : true,
36966         /**
36967          * @event dragout
36968          * Fires when the dragged row(s) leave another DD target while being dragged
36969          * @param {Grid} this
36970          * @param {Roo.GridDD} dd The drag drop object
36971          * @param {String} targetId The target drag drop object
36972          * @param {event} e The raw browser event
36973          */
36974         "dragout" : true,
36975         /**
36976          * @event rowclass
36977          * Fires when a row is rendered, so you can change add a style to it.
36978          * @param {GridView} gridview   The grid view
36979          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
36980          */
36981         'rowclass' : true,
36982
36983         /**
36984          * @event render
36985          * Fires when the grid is rendered
36986          * @param {Grid} grid
36987          */
36988         'render' : true,
36989             /**
36990              * @event select
36991              * Fires when a date is selected
36992              * @param {DatePicker} this
36993              * @param {Date} date The selected date
36994              */
36995         'select': true,
36996         /**
36997              * @event monthchange
36998              * Fires when the displayed month changes 
36999              * @param {DatePicker} this
37000              * @param {Date} date The selected month
37001              */
37002         'monthchange': true,
37003         /**
37004              * @event evententer
37005              * Fires when mouse over an event
37006              * @param {Calendar} this
37007              * @param {event} Event
37008              */
37009         'evententer': true,
37010         /**
37011              * @event eventleave
37012              * Fires when the mouse leaves an
37013              * @param {Calendar} this
37014              * @param {event}
37015              */
37016         'eventleave': true,
37017         /**
37018              * @event eventclick
37019              * Fires when the mouse click an
37020              * @param {Calendar} this
37021              * @param {event}
37022              */
37023         'eventclick': true,
37024         /**
37025              * @event eventrender
37026              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
37027              * @param {Calendar} this
37028              * @param {data} data to be modified
37029              */
37030         'eventrender': true
37031         
37032     });
37033
37034     Roo.grid.Grid.superclass.constructor.call(this);
37035     this.on('render', function() {
37036         this.view.el.addClass('x-grid-cal'); 
37037         
37038         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
37039
37040     },this);
37041     
37042     if (!Roo.grid.Calendar.style) {
37043         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
37044             
37045             
37046             '.x-grid-cal .x-grid-col' :  {
37047                 height: 'auto !important',
37048                 'vertical-align': 'top'
37049             },
37050             '.x-grid-cal  .fc-event-hori' : {
37051                 height: '14px'
37052             }
37053              
37054             
37055         }, Roo.id());
37056     }
37057
37058     
37059     
37060 };
37061 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
37062     /**
37063      * @cfg {Store} eventStore The store that loads events.
37064      */
37065     eventStore : 25,
37066
37067      
37068     activeDate : false,
37069     startDay : 0,
37070     autoWidth : true,
37071     monitorWindowResize : false,
37072
37073     
37074     resizeColumns : function() {
37075         var col = (this.view.el.getWidth() / 7) - 3;
37076         // loop through cols, and setWidth
37077         for(var i =0 ; i < 7 ; i++){
37078             this.cm.setColumnWidth(i, col);
37079         }
37080     },
37081      setDate :function(date) {
37082         
37083         Roo.log('setDate?');
37084         
37085         this.resizeColumns();
37086         var vd = this.activeDate;
37087         this.activeDate = date;
37088 //        if(vd && this.el){
37089 //            var t = date.getTime();
37090 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
37091 //                Roo.log('using add remove');
37092 //                
37093 //                this.fireEvent('monthchange', this, date);
37094 //                
37095 //                this.cells.removeClass("fc-state-highlight");
37096 //                this.cells.each(function(c){
37097 //                   if(c.dateValue == t){
37098 //                       c.addClass("fc-state-highlight");
37099 //                       setTimeout(function(){
37100 //                            try{c.dom.firstChild.focus();}catch(e){}
37101 //                       }, 50);
37102 //                       return false;
37103 //                   }
37104 //                   return true;
37105 //                });
37106 //                return;
37107 //            }
37108 //        }
37109         
37110         var days = date.getDaysInMonth();
37111         
37112         var firstOfMonth = date.getFirstDateOfMonth();
37113         var startingPos = firstOfMonth.getDay()-this.startDay;
37114         
37115         if(startingPos < this.startDay){
37116             startingPos += 7;
37117         }
37118         
37119         var pm = date.add(Date.MONTH, -1);
37120         var prevStart = pm.getDaysInMonth()-startingPos;
37121 //        
37122         
37123         
37124         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37125         
37126         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
37127         //this.cells.addClassOnOver('fc-state-hover');
37128         
37129         var cells = this.cells.elements;
37130         var textEls = this.textNodes;
37131         
37132         //Roo.each(cells, function(cell){
37133         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
37134         //});
37135         
37136         days += startingPos;
37137
37138         // convert everything to numbers so it's fast
37139         var day = 86400000;
37140         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
37141         //Roo.log(d);
37142         //Roo.log(pm);
37143         //Roo.log(prevStart);
37144         
37145         var today = new Date().clearTime().getTime();
37146         var sel = date.clearTime().getTime();
37147         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
37148         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
37149         var ddMatch = this.disabledDatesRE;
37150         var ddText = this.disabledDatesText;
37151         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
37152         var ddaysText = this.disabledDaysText;
37153         var format = this.format;
37154         
37155         var setCellClass = function(cal, cell){
37156             
37157             //Roo.log('set Cell Class');
37158             cell.title = "";
37159             var t = d.getTime();
37160             
37161             //Roo.log(d);
37162             
37163             
37164             cell.dateValue = t;
37165             if(t == today){
37166                 cell.className += " fc-today";
37167                 cell.className += " fc-state-highlight";
37168                 cell.title = cal.todayText;
37169             }
37170             if(t == sel){
37171                 // disable highlight in other month..
37172                 cell.className += " fc-state-highlight";
37173                 
37174             }
37175             // disabling
37176             if(t < min) {
37177                 //cell.className = " fc-state-disabled";
37178                 cell.title = cal.minText;
37179                 return;
37180             }
37181             if(t > max) {
37182                 //cell.className = " fc-state-disabled";
37183                 cell.title = cal.maxText;
37184                 return;
37185             }
37186             if(ddays){
37187                 if(ddays.indexOf(d.getDay()) != -1){
37188                     // cell.title = ddaysText;
37189                    // cell.className = " fc-state-disabled";
37190                 }
37191             }
37192             if(ddMatch && format){
37193                 var fvalue = d.dateFormat(format);
37194                 if(ddMatch.test(fvalue)){
37195                     cell.title = ddText.replace("%0", fvalue);
37196                    cell.className = " fc-state-disabled";
37197                 }
37198             }
37199             
37200             if (!cell.initialClassName) {
37201                 cell.initialClassName = cell.dom.className;
37202             }
37203             
37204             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
37205         };
37206
37207         var i = 0;
37208         
37209         for(; i < startingPos; i++) {
37210             cells[i].dayName =  (++prevStart);
37211             Roo.log(textEls[i]);
37212             d.setDate(d.getDate()+1);
37213             
37214             //cells[i].className = "fc-past fc-other-month";
37215             setCellClass(this, cells[i]);
37216         }
37217         
37218         var intDay = 0;
37219         
37220         for(; i < days; i++){
37221             intDay = i - startingPos + 1;
37222             cells[i].dayName =  (intDay);
37223             d.setDate(d.getDate()+1);
37224             
37225             cells[i].className = ''; // "x-date-active";
37226             setCellClass(this, cells[i]);
37227         }
37228         var extraDays = 0;
37229         
37230         for(; i < 42; i++) {
37231             //textEls[i].innerHTML = (++extraDays);
37232             
37233             d.setDate(d.getDate()+1);
37234             cells[i].dayName = (++extraDays);
37235             cells[i].className = "fc-future fc-other-month";
37236             setCellClass(this, cells[i]);
37237         }
37238         
37239         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
37240         
37241         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
37242         
37243         // this will cause all the cells to mis
37244         var rows= [];
37245         var i =0;
37246         for (var r = 0;r < 6;r++) {
37247             for (var c =0;c < 7;c++) {
37248                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
37249             }    
37250         }
37251         
37252         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37253         for(i=0;i<cells.length;i++) {
37254             
37255             this.cells.elements[i].dayName = cells[i].dayName ;
37256             this.cells.elements[i].className = cells[i].className;
37257             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
37258             this.cells.elements[i].title = cells[i].title ;
37259             this.cells.elements[i].dateValue = cells[i].dateValue ;
37260         }
37261         
37262         
37263         
37264         
37265         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
37266         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
37267         
37268         ////if(totalRows != 6){
37269             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
37270            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
37271        // }
37272         
37273         this.fireEvent('monthchange', this, date);
37274         
37275         
37276     },
37277  /**
37278      * Returns the grid's SelectionModel.
37279      * @return {SelectionModel}
37280      */
37281     getSelectionModel : function(){
37282         if(!this.selModel){
37283             this.selModel = new Roo.grid.CellSelectionModel();
37284         }
37285         return this.selModel;
37286     },
37287
37288     load: function() {
37289         this.eventStore.load()
37290         
37291         
37292         
37293     },
37294     
37295     findCell : function(dt) {
37296         dt = dt.clearTime().getTime();
37297         var ret = false;
37298         this.cells.each(function(c){
37299             //Roo.log("check " +c.dateValue + '?=' + dt);
37300             if(c.dateValue == dt){
37301                 ret = c;
37302                 return false;
37303             }
37304             return true;
37305         });
37306         
37307         return ret;
37308     },
37309     
37310     findCells : function(rec) {
37311         var s = rec.data.start_dt.clone().clearTime().getTime();
37312        // Roo.log(s);
37313         var e= rec.data.end_dt.clone().clearTime().getTime();
37314        // Roo.log(e);
37315         var ret = [];
37316         this.cells.each(function(c){
37317              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
37318             
37319             if(c.dateValue > e){
37320                 return ;
37321             }
37322             if(c.dateValue < s){
37323                 return ;
37324             }
37325             ret.push(c);
37326         });
37327         
37328         return ret;    
37329     },
37330     
37331     findBestRow: function(cells)
37332     {
37333         var ret = 0;
37334         
37335         for (var i =0 ; i < cells.length;i++) {
37336             ret  = Math.max(cells[i].rows || 0,ret);
37337         }
37338         return ret;
37339         
37340     },
37341     
37342     
37343     addItem : function(rec)
37344     {
37345         // look for vertical location slot in
37346         var cells = this.findCells(rec);
37347         
37348         rec.row = this.findBestRow(cells);
37349         
37350         // work out the location.
37351         
37352         var crow = false;
37353         var rows = [];
37354         for(var i =0; i < cells.length; i++) {
37355             if (!crow) {
37356                 crow = {
37357                     start : cells[i],
37358                     end :  cells[i]
37359                 };
37360                 continue;
37361             }
37362             if (crow.start.getY() == cells[i].getY()) {
37363                 // on same row.
37364                 crow.end = cells[i];
37365                 continue;
37366             }
37367             // different row.
37368             rows.push(crow);
37369             crow = {
37370                 start: cells[i],
37371                 end : cells[i]
37372             };
37373             
37374         }
37375         
37376         rows.push(crow);
37377         rec.els = [];
37378         rec.rows = rows;
37379         rec.cells = cells;
37380         for (var i = 0; i < cells.length;i++) {
37381             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
37382             
37383         }
37384         
37385         
37386     },
37387     
37388     clearEvents: function() {
37389         
37390         if (!this.eventStore.getCount()) {
37391             return;
37392         }
37393         // reset number of rows in cells.
37394         Roo.each(this.cells.elements, function(c){
37395             c.rows = 0;
37396         });
37397         
37398         this.eventStore.each(function(e) {
37399             this.clearEvent(e);
37400         },this);
37401         
37402     },
37403     
37404     clearEvent : function(ev)
37405     {
37406         if (ev.els) {
37407             Roo.each(ev.els, function(el) {
37408                 el.un('mouseenter' ,this.onEventEnter, this);
37409                 el.un('mouseleave' ,this.onEventLeave, this);
37410                 el.remove();
37411             },this);
37412             ev.els = [];
37413         }
37414     },
37415     
37416     
37417     renderEvent : function(ev,ctr) {
37418         if (!ctr) {
37419              ctr = this.view.el.select('.fc-event-container',true).first();
37420         }
37421         
37422          
37423         this.clearEvent(ev);
37424             //code
37425        
37426         
37427         
37428         ev.els = [];
37429         var cells = ev.cells;
37430         var rows = ev.rows;
37431         this.fireEvent('eventrender', this, ev);
37432         
37433         for(var i =0; i < rows.length; i++) {
37434             
37435             cls = '';
37436             if (i == 0) {
37437                 cls += ' fc-event-start';
37438             }
37439             if ((i+1) == rows.length) {
37440                 cls += ' fc-event-end';
37441             }
37442             
37443             //Roo.log(ev.data);
37444             // how many rows should it span..
37445             var cg = this.eventTmpl.append(ctr,Roo.apply({
37446                 fccls : cls
37447                 
37448             }, ev.data) , true);
37449             
37450             
37451             cg.on('mouseenter' ,this.onEventEnter, this, ev);
37452             cg.on('mouseleave' ,this.onEventLeave, this, ev);
37453             cg.on('click', this.onEventClick, this, ev);
37454             
37455             ev.els.push(cg);
37456             
37457             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
37458             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
37459             //Roo.log(cg);
37460              
37461             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
37462             cg.setWidth(ebox.right - sbox.x -2);
37463         }
37464     },
37465     
37466     renderEvents: function()
37467     {   
37468         // first make sure there is enough space..
37469         
37470         if (!this.eventTmpl) {
37471             this.eventTmpl = new Roo.Template(
37472                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
37473                     '<div class="fc-event-inner">' +
37474                         '<span class="fc-event-time">{time}</span>' +
37475                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
37476                     '</div>' +
37477                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
37478                 '</div>'
37479             );
37480                 
37481         }
37482                
37483         
37484         
37485         this.cells.each(function(c) {
37486             //Roo.log(c.select('.fc-day-content div',true).first());
37487             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
37488         });
37489         
37490         var ctr = this.view.el.select('.fc-event-container',true).first();
37491         
37492         var cls;
37493         this.eventStore.each(function(ev){
37494             
37495             this.renderEvent(ev);
37496              
37497              
37498         }, this);
37499         this.view.layout();
37500         
37501     },
37502     
37503     onEventEnter: function (e, el,event,d) {
37504         this.fireEvent('evententer', this, el, event);
37505     },
37506     
37507     onEventLeave: function (e, el,event,d) {
37508         this.fireEvent('eventleave', this, el, event);
37509     },
37510     
37511     onEventClick: function (e, el,event,d) {
37512         this.fireEvent('eventclick', this, el, event);
37513     },
37514     
37515     onMonthChange: function () {
37516         this.store.load();
37517     },
37518     
37519     onLoad: function () {
37520         
37521         //Roo.log('calendar onload');
37522 //         
37523         if(this.eventStore.getCount() > 0){
37524             
37525            
37526             
37527             this.eventStore.each(function(d){
37528                 
37529                 
37530                 // FIXME..
37531                 var add =   d.data;
37532                 if (typeof(add.end_dt) == 'undefined')  {
37533                     Roo.log("Missing End time in calendar data: ");
37534                     Roo.log(d);
37535                     return;
37536                 }
37537                 if (typeof(add.start_dt) == 'undefined')  {
37538                     Roo.log("Missing Start time in calendar data: ");
37539                     Roo.log(d);
37540                     return;
37541                 }
37542                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
37543                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
37544                 add.id = add.id || d.id;
37545                 add.title = add.title || '??';
37546                 
37547                 this.addItem(d);
37548                 
37549              
37550             },this);
37551         }
37552         
37553         this.renderEvents();
37554     }
37555     
37556
37557 });
37558 /*
37559  grid : {
37560                 xtype: 'Grid',
37561                 xns: Roo.grid,
37562                 listeners : {
37563                     render : function ()
37564                     {
37565                         _this.grid = this;
37566                         
37567                         if (!this.view.el.hasClass('course-timesheet')) {
37568                             this.view.el.addClass('course-timesheet');
37569                         }
37570                         if (this.tsStyle) {
37571                             this.ds.load({});
37572                             return; 
37573                         }
37574                         Roo.log('width');
37575                         Roo.log(_this.grid.view.el.getWidth());
37576                         
37577                         
37578                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
37579                             '.course-timesheet .x-grid-row' : {
37580                                 height: '80px'
37581                             },
37582                             '.x-grid-row td' : {
37583                                 'vertical-align' : 0
37584                             },
37585                             '.course-edit-link' : {
37586                                 'color' : 'blue',
37587                                 'text-overflow' : 'ellipsis',
37588                                 'overflow' : 'hidden',
37589                                 'white-space' : 'nowrap',
37590                                 'cursor' : 'pointer'
37591                             },
37592                             '.sub-link' : {
37593                                 'color' : 'green'
37594                             },
37595                             '.de-act-sup-link' : {
37596                                 'color' : 'purple',
37597                                 'text-decoration' : 'line-through'
37598                             },
37599                             '.de-act-link' : {
37600                                 'color' : 'red',
37601                                 'text-decoration' : 'line-through'
37602                             },
37603                             '.course-timesheet .course-highlight' : {
37604                                 'border-top-style': 'dashed !important',
37605                                 'border-bottom-bottom': 'dashed !important'
37606                             },
37607                             '.course-timesheet .course-item' : {
37608                                 'font-family'   : 'tahoma, arial, helvetica',
37609                                 'font-size'     : '11px',
37610                                 'overflow'      : 'hidden',
37611                                 'padding-left'  : '10px',
37612                                 'padding-right' : '10px',
37613                                 'padding-top' : '10px' 
37614                             }
37615                             
37616                         }, Roo.id());
37617                                 this.ds.load({});
37618                     }
37619                 },
37620                 autoWidth : true,
37621                 monitorWindowResize : false,
37622                 cellrenderer : function(v,x,r)
37623                 {
37624                     return v;
37625                 },
37626                 sm : {
37627                     xtype: 'CellSelectionModel',
37628                     xns: Roo.grid
37629                 },
37630                 dataSource : {
37631                     xtype: 'Store',
37632                     xns: Roo.data,
37633                     listeners : {
37634                         beforeload : function (_self, options)
37635                         {
37636                             options.params = options.params || {};
37637                             options.params._month = _this.monthField.getValue();
37638                             options.params.limit = 9999;
37639                             options.params['sort'] = 'when_dt';    
37640                             options.params['dir'] = 'ASC';    
37641                             this.proxy.loadResponse = this.loadResponse;
37642                             Roo.log("load?");
37643                             //this.addColumns();
37644                         },
37645                         load : function (_self, records, options)
37646                         {
37647                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
37648                                 // if you click on the translation.. you can edit it...
37649                                 var el = Roo.get(this);
37650                                 var id = el.dom.getAttribute('data-id');
37651                                 var d = el.dom.getAttribute('data-date');
37652                                 var t = el.dom.getAttribute('data-time');
37653                                 //var id = this.child('span').dom.textContent;
37654                                 
37655                                 //Roo.log(this);
37656                                 Pman.Dialog.CourseCalendar.show({
37657                                     id : id,
37658                                     when_d : d,
37659                                     when_t : t,
37660                                     productitem_active : id ? 1 : 0
37661                                 }, function() {
37662                                     _this.grid.ds.load({});
37663                                 });
37664                            
37665                            });
37666                            
37667                            _this.panel.fireEvent('resize', [ '', '' ]);
37668                         }
37669                     },
37670                     loadResponse : function(o, success, response){
37671                             // this is overridden on before load..
37672                             
37673                             Roo.log("our code?");       
37674                             //Roo.log(success);
37675                             //Roo.log(response)
37676                             delete this.activeRequest;
37677                             if(!success){
37678                                 this.fireEvent("loadexception", this, o, response);
37679                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
37680                                 return;
37681                             }
37682                             var result;
37683                             try {
37684                                 result = o.reader.read(response);
37685                             }catch(e){
37686                                 Roo.log("load exception?");
37687                                 this.fireEvent("loadexception", this, o, response, e);
37688                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
37689                                 return;
37690                             }
37691                             Roo.log("ready...");        
37692                             // loop through result.records;
37693                             // and set this.tdate[date] = [] << array of records..
37694                             _this.tdata  = {};
37695                             Roo.each(result.records, function(r){
37696                                 //Roo.log(r.data);
37697                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
37698                                     _this.tdata[r.data.when_dt.format('j')] = [];
37699                                 }
37700                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
37701                             });
37702                             
37703                             //Roo.log(_this.tdata);
37704                             
37705                             result.records = [];
37706                             result.totalRecords = 6;
37707                     
37708                             // let's generate some duumy records for the rows.
37709                             //var st = _this.dateField.getValue();
37710                             
37711                             // work out monday..
37712                             //st = st.add(Date.DAY, -1 * st.format('w'));
37713                             
37714                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
37715                             
37716                             var firstOfMonth = date.getFirstDayOfMonth();
37717                             var days = date.getDaysInMonth();
37718                             var d = 1;
37719                             var firstAdded = false;
37720                             for (var i = 0; i < result.totalRecords ; i++) {
37721                                 //var d= st.add(Date.DAY, i);
37722                                 var row = {};
37723                                 var added = 0;
37724                                 for(var w = 0 ; w < 7 ; w++){
37725                                     if(!firstAdded && firstOfMonth != w){
37726                                         continue;
37727                                     }
37728                                     if(d > days){
37729                                         continue;
37730                                     }
37731                                     firstAdded = true;
37732                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
37733                                     row['weekday'+w] = String.format(
37734                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
37735                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
37736                                                     d,
37737                                                     date.format('Y-m-')+dd
37738                                                 );
37739                                     added++;
37740                                     if(typeof(_this.tdata[d]) != 'undefined'){
37741                                         Roo.each(_this.tdata[d], function(r){
37742                                             var is_sub = '';
37743                                             var deactive = '';
37744                                             var id = r.id;
37745                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
37746                                             if(r.parent_id*1>0){
37747                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
37748                                                 id = r.parent_id;
37749                                             }
37750                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
37751                                                 deactive = 'de-act-link';
37752                                             }
37753                                             
37754                                             row['weekday'+w] += String.format(
37755                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
37756                                                     id, //0
37757                                                     r.product_id_name, //1
37758                                                     r.when_dt.format('h:ia'), //2
37759                                                     is_sub, //3
37760                                                     deactive, //4
37761                                                     desc // 5
37762                                             );
37763                                         });
37764                                     }
37765                                     d++;
37766                                 }
37767                                 
37768                                 // only do this if something added..
37769                                 if(added > 0){ 
37770                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
37771                                 }
37772                                 
37773                                 
37774                                 // push it twice. (second one with an hour..
37775                                 
37776                             }
37777                             //Roo.log(result);
37778                             this.fireEvent("load", this, o, o.request.arg);
37779                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
37780                         },
37781                     sortInfo : {field: 'when_dt', direction : 'ASC' },
37782                     proxy : {
37783                         xtype: 'HttpProxy',
37784                         xns: Roo.data,
37785                         method : 'GET',
37786                         url : baseURL + '/Roo/Shop_course.php'
37787                     },
37788                     reader : {
37789                         xtype: 'JsonReader',
37790                         xns: Roo.data,
37791                         id : 'id',
37792                         fields : [
37793                             {
37794                                 'name': 'id',
37795                                 'type': 'int'
37796                             },
37797                             {
37798                                 'name': 'when_dt',
37799                                 'type': 'string'
37800                             },
37801                             {
37802                                 'name': 'end_dt',
37803                                 'type': 'string'
37804                             },
37805                             {
37806                                 'name': 'parent_id',
37807                                 'type': 'int'
37808                             },
37809                             {
37810                                 'name': 'product_id',
37811                                 'type': 'int'
37812                             },
37813                             {
37814                                 'name': 'productitem_id',
37815                                 'type': 'int'
37816                             },
37817                             {
37818                                 'name': 'guid',
37819                                 'type': 'int'
37820                             }
37821                         ]
37822                     }
37823                 },
37824                 toolbar : {
37825                     xtype: 'Toolbar',
37826                     xns: Roo,
37827                     items : [
37828                         {
37829                             xtype: 'Button',
37830                             xns: Roo.Toolbar,
37831                             listeners : {
37832                                 click : function (_self, e)
37833                                 {
37834                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
37835                                     sd.setMonth(sd.getMonth()-1);
37836                                     _this.monthField.setValue(sd.format('Y-m-d'));
37837                                     _this.grid.ds.load({});
37838                                 }
37839                             },
37840                             text : "Back"
37841                         },
37842                         {
37843                             xtype: 'Separator',
37844                             xns: Roo.Toolbar
37845                         },
37846                         {
37847                             xtype: 'MonthField',
37848                             xns: Roo.form,
37849                             listeners : {
37850                                 render : function (_self)
37851                                 {
37852                                     _this.monthField = _self;
37853                                    // _this.monthField.set  today
37854                                 },
37855                                 select : function (combo, date)
37856                                 {
37857                                     _this.grid.ds.load({});
37858                                 }
37859                             },
37860                             value : (function() { return new Date(); })()
37861                         },
37862                         {
37863                             xtype: 'Separator',
37864                             xns: Roo.Toolbar
37865                         },
37866                         {
37867                             xtype: 'TextItem',
37868                             xns: Roo.Toolbar,
37869                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
37870                         },
37871                         {
37872                             xtype: 'Fill',
37873                             xns: Roo.Toolbar
37874                         },
37875                         {
37876                             xtype: 'Button',
37877                             xns: Roo.Toolbar,
37878                             listeners : {
37879                                 click : function (_self, e)
37880                                 {
37881                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
37882                                     sd.setMonth(sd.getMonth()+1);
37883                                     _this.monthField.setValue(sd.format('Y-m-d'));
37884                                     _this.grid.ds.load({});
37885                                 }
37886                             },
37887                             text : "Next"
37888                         }
37889                     ]
37890                 },
37891                  
37892             }
37893         };
37894         
37895         *//*
37896  * Based on:
37897  * Ext JS Library 1.1.1
37898  * Copyright(c) 2006-2007, Ext JS, LLC.
37899  *
37900  * Originally Released Under LGPL - original licence link has changed is not relivant.
37901  *
37902  * Fork - LGPL
37903  * <script type="text/javascript">
37904  */
37905  
37906 /**
37907  * @class Roo.LoadMask
37908  * A simple utility class for generically masking elements while loading data.  If the element being masked has
37909  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
37910  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
37911  * element's UpdateManager load indicator and will be destroyed after the initial load.
37912  * @constructor
37913  * Create a new LoadMask
37914  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
37915  * @param {Object} config The config object
37916  */
37917 Roo.LoadMask = function(el, config){
37918     this.el = Roo.get(el);
37919     Roo.apply(this, config);
37920     if(this.store){
37921         this.store.on('beforeload', this.onBeforeLoad, this);
37922         this.store.on('load', this.onLoad, this);
37923         this.store.on('loadexception', this.onLoadException, this);
37924         this.removeMask = false;
37925     }else{
37926         var um = this.el.getUpdateManager();
37927         um.showLoadIndicator = false; // disable the default indicator
37928         um.on('beforeupdate', this.onBeforeLoad, this);
37929         um.on('update', this.onLoad, this);
37930         um.on('failure', this.onLoad, this);
37931         this.removeMask = true;
37932     }
37933 };
37934
37935 Roo.LoadMask.prototype = {
37936     /**
37937      * @cfg {Boolean} removeMask
37938      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
37939      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
37940      */
37941     /**
37942      * @cfg {String} msg
37943      * The text to display in a centered loading message box (defaults to 'Loading...')
37944      */
37945     msg : 'Loading...',
37946     /**
37947      * @cfg {String} msgCls
37948      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
37949      */
37950     msgCls : 'x-mask-loading',
37951
37952     /**
37953      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
37954      * @type Boolean
37955      */
37956     disabled: false,
37957
37958     /**
37959      * Disables the mask to prevent it from being displayed
37960      */
37961     disable : function(){
37962        this.disabled = true;
37963     },
37964
37965     /**
37966      * Enables the mask so that it can be displayed
37967      */
37968     enable : function(){
37969         this.disabled = false;
37970     },
37971     
37972     onLoadException : function()
37973     {
37974         Roo.log(arguments);
37975         
37976         if (typeof(arguments[3]) != 'undefined') {
37977             Roo.MessageBox.alert("Error loading",arguments[3]);
37978         } 
37979         /*
37980         try {
37981             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
37982                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
37983             }   
37984         } catch(e) {
37985             
37986         }
37987         */
37988     
37989         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
37990     },
37991     // private
37992     onLoad : function()
37993     {
37994         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
37995     },
37996
37997     // private
37998     onBeforeLoad : function(){
37999         if(!this.disabled){
38000             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
38001         }
38002     },
38003
38004     // private
38005     destroy : function(){
38006         if(this.store){
38007             this.store.un('beforeload', this.onBeforeLoad, this);
38008             this.store.un('load', this.onLoad, this);
38009             this.store.un('loadexception', this.onLoadException, this);
38010         }else{
38011             var um = this.el.getUpdateManager();
38012             um.un('beforeupdate', this.onBeforeLoad, this);
38013             um.un('update', this.onLoad, this);
38014             um.un('failure', this.onLoad, this);
38015         }
38016     }
38017 };/*
38018  * Based on:
38019  * Ext JS Library 1.1.1
38020  * Copyright(c) 2006-2007, Ext JS, LLC.
38021  *
38022  * Originally Released Under LGPL - original licence link has changed is not relivant.
38023  *
38024  * Fork - LGPL
38025  * <script type="text/javascript">
38026  */
38027
38028
38029 /**
38030  * @class Roo.XTemplate
38031  * @extends Roo.Template
38032  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
38033 <pre><code>
38034 var t = new Roo.XTemplate(
38035         '&lt;select name="{name}"&gt;',
38036                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
38037         '&lt;/select&gt;'
38038 );
38039  
38040 // then append, applying the master template values
38041  </code></pre>
38042  *
38043  * Supported features:
38044  *
38045  *  Tags:
38046
38047 <pre><code>
38048       {a_variable} - output encoded.
38049       {a_variable.format:("Y-m-d")} - call a method on the variable
38050       {a_variable:raw} - unencoded output
38051       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
38052       {a_variable:this.method_on_template(...)} - call a method on the template object.
38053  
38054 </code></pre>
38055  *  The tpl tag:
38056 <pre><code>
38057         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
38058         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
38059         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
38060         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
38061   
38062         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
38063         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
38064 </code></pre>
38065  *      
38066  */
38067 Roo.XTemplate = function()
38068 {
38069     Roo.XTemplate.superclass.constructor.apply(this, arguments);
38070     if (this.html) {
38071         this.compile();
38072     }
38073 };
38074
38075
38076 Roo.extend(Roo.XTemplate, Roo.Template, {
38077
38078     /**
38079      * The various sub templates
38080      */
38081     tpls : false,
38082     /**
38083      *
38084      * basic tag replacing syntax
38085      * WORD:WORD()
38086      *
38087      * // you can fake an object call by doing this
38088      *  x.t:(test,tesT) 
38089      * 
38090      */
38091     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
38092
38093     /**
38094      * compile the template
38095      *
38096      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
38097      *
38098      */
38099     compile: function()
38100     {
38101         var s = this.html;
38102      
38103         s = ['<tpl>', s, '</tpl>'].join('');
38104     
38105         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
38106             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
38107             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
38108             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
38109             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
38110             m,
38111             id     = 0,
38112             tpls   = [];
38113     
38114         while(true == !!(m = s.match(re))){
38115             var forMatch   = m[0].match(nameRe),
38116                 ifMatch   = m[0].match(ifRe),
38117                 execMatch   = m[0].match(execRe),
38118                 namedMatch   = m[0].match(namedRe),
38119                 
38120                 exp  = null, 
38121                 fn   = null,
38122                 exec = null,
38123                 name = forMatch && forMatch[1] ? forMatch[1] : '';
38124                 
38125             if (ifMatch) {
38126                 // if - puts fn into test..
38127                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
38128                 if(exp){
38129                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
38130                 }
38131             }
38132             
38133             if (execMatch) {
38134                 // exec - calls a function... returns empty if true is  returned.
38135                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
38136                 if(exp){
38137                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
38138                 }
38139             }
38140             
38141             
38142             if (name) {
38143                 // for = 
38144                 switch(name){
38145                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
38146                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
38147                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
38148                 }
38149             }
38150             var uid = namedMatch ? namedMatch[1] : id;
38151             
38152             
38153             tpls.push({
38154                 id:     namedMatch ? namedMatch[1] : id,
38155                 target: name,
38156                 exec:   exec,
38157                 test:   fn,
38158                 body:   m[1] || ''
38159             });
38160             if (namedMatch) {
38161                 s = s.replace(m[0], '');
38162             } else { 
38163                 s = s.replace(m[0], '{xtpl'+ id + '}');
38164             }
38165             ++id;
38166         }
38167         this.tpls = [];
38168         for(var i = tpls.length-1; i >= 0; --i){
38169             this.compileTpl(tpls[i]);
38170             this.tpls[tpls[i].id] = tpls[i];
38171         }
38172         this.master = tpls[tpls.length-1];
38173         return this;
38174     },
38175     /**
38176      * same as applyTemplate, except it's done to one of the subTemplates
38177      * when using named templates, you can do:
38178      *
38179      * var str = pl.applySubTemplate('your-name', values);
38180      *
38181      * 
38182      * @param {Number} id of the template
38183      * @param {Object} values to apply to template
38184      * @param {Object} parent (normaly the instance of this object)
38185      */
38186     applySubTemplate : function(id, values, parent)
38187     {
38188         
38189         
38190         var t = this.tpls[id];
38191         
38192         
38193         try { 
38194             if(t.test && !t.test.call(this, values, parent)){
38195                 return '';
38196             }
38197         } catch(e) {
38198             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
38199             Roo.log(e.toString());
38200             Roo.log(t.test);
38201             return ''
38202         }
38203         try { 
38204             
38205             if(t.exec && t.exec.call(this, values, parent)){
38206                 return '';
38207             }
38208         } catch(e) {
38209             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
38210             Roo.log(e.toString());
38211             Roo.log(t.exec);
38212             return ''
38213         }
38214         try {
38215             var vs = t.target ? t.target.call(this, values, parent) : values;
38216             parent = t.target ? values : parent;
38217             if(t.target && vs instanceof Array){
38218                 var buf = [];
38219                 for(var i = 0, len = vs.length; i < len; i++){
38220                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
38221                 }
38222                 return buf.join('');
38223             }
38224             return t.compiled.call(this, vs, parent);
38225         } catch (e) {
38226             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
38227             Roo.log(e.toString());
38228             Roo.log(t.compiled);
38229             return '';
38230         }
38231     },
38232
38233     compileTpl : function(tpl)
38234     {
38235         var fm = Roo.util.Format;
38236         var useF = this.disableFormats !== true;
38237         var sep = Roo.isGecko ? "+" : ",";
38238         var undef = function(str) {
38239             Roo.log("Property not found :"  + str);
38240             return '';
38241         };
38242         
38243         var fn = function(m, name, format, args)
38244         {
38245             //Roo.log(arguments);
38246             args = args ? args.replace(/\\'/g,"'") : args;
38247             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
38248             if (typeof(format) == 'undefined') {
38249                 format= 'htmlEncode';
38250             }
38251             if (format == 'raw' ) {
38252                 format = false;
38253             }
38254             
38255             if(name.substr(0, 4) == 'xtpl'){
38256                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
38257             }
38258             
38259             // build an array of options to determine if value is undefined..
38260             
38261             // basically get 'xxxx.yyyy' then do
38262             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
38263             //    (function () { Roo.log("Property not found"); return ''; })() :
38264             //    ......
38265             
38266             var udef_ar = [];
38267             var lookfor = '';
38268             Roo.each(name.split('.'), function(st) {
38269                 lookfor += (lookfor.length ? '.': '') + st;
38270                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
38271             });
38272             
38273             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
38274             
38275             
38276             if(format && useF){
38277                 
38278                 args = args ? ',' + args : "";
38279                  
38280                 if(format.substr(0, 5) != "this."){
38281                     format = "fm." + format + '(';
38282                 }else{
38283                     format = 'this.call("'+ format.substr(5) + '", ';
38284                     args = ", values";
38285                 }
38286                 
38287                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
38288             }
38289              
38290             if (args.length) {
38291                 // called with xxyx.yuu:(test,test)
38292                 // change to ()
38293                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
38294             }
38295             // raw.. - :raw modifier..
38296             return "'"+ sep + udef_st  + name + ")"+sep+"'";
38297             
38298         };
38299         var body;
38300         // branched to use + in gecko and [].join() in others
38301         if(Roo.isGecko){
38302             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
38303                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
38304                     "';};};";
38305         }else{
38306             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
38307             body.push(tpl.body.replace(/(\r\n|\n)/g,
38308                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
38309             body.push("'].join('');};};");
38310             body = body.join('');
38311         }
38312         
38313         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
38314        
38315         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
38316         eval(body);
38317         
38318         return this;
38319     },
38320
38321     applyTemplate : function(values){
38322         return this.master.compiled.call(this, values, {});
38323         //var s = this.subs;
38324     },
38325
38326     apply : function(){
38327         return this.applyTemplate.apply(this, arguments);
38328     }
38329
38330  });
38331
38332 Roo.XTemplate.from = function(el){
38333     el = Roo.getDom(el);
38334     return new Roo.XTemplate(el.value || el.innerHTML);
38335 };