Fix #5718 - Change the order / layout of tabs. / new master lists
[roojs1] / roojs-ui-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11
12
13 /**
14  * @class Roo.data.SortTypes
15  * @singleton
16  * Defines the default sorting (casting?) comparison functions used when sorting data.
17  */
18 Roo.data.SortTypes = {
19     /**
20      * Default sort that does nothing
21      * @param {Mixed} s The value being converted
22      * @return {Mixed} The comparison value
23      */
24     none : function(s){
25         return s;
26     },
27     
28     /**
29      * The regular expression used to strip tags
30      * @type {RegExp}
31      * @property
32      */
33     stripTagsRE : /<\/?[^>]+>/gi,
34     
35     /**
36      * Strips all HTML tags to sort on text only
37      * @param {Mixed} s The value being converted
38      * @return {String} The comparison value
39      */
40     asText : function(s){
41         return String(s).replace(this.stripTagsRE, "");
42     },
43     
44     /**
45      * Strips all HTML tags to sort on text only - Case insensitive
46      * @param {Mixed} s The value being converted
47      * @return {String} The comparison value
48      */
49     asUCText : function(s){
50         return String(s).toUpperCase().replace(this.stripTagsRE, "");
51     },
52     
53     /**
54      * Case insensitive string
55      * @param {Mixed} s The value being converted
56      * @return {String} The comparison value
57      */
58     asUCString : function(s) {
59         return String(s).toUpperCase();
60     },
61     
62     /**
63      * Date sorting
64      * @param {Mixed} s The value being converted
65      * @return {Number} The comparison value
66      */
67     asDate : function(s) {
68         if(!s){
69             return 0;
70         }
71         if(s instanceof Date){
72             return s.getTime();
73         }
74         return Date.parse(String(s));
75     },
76     
77     /**
78      * Float sorting
79      * @param {Mixed} s The value being converted
80      * @return {Float} The comparison value
81      */
82     asFloat : function(s) {
83         var val = parseFloat(String(s).replace(/,/g, ""));
84         if(isNaN(val)) {
85             val = 0;
86         }
87         return val;
88     },
89     
90     /**
91      * Integer sorting
92      * @param {Mixed} s The value being converted
93      * @return {Number} The comparison value
94      */
95     asInt : function(s) {
96         var val = parseInt(String(s).replace(/,/g, ""));
97         if(isNaN(val)) {
98             val = 0;
99         }
100         return val;
101     }
102 };/*
103  * Based on:
104  * Ext JS Library 1.1.1
105  * Copyright(c) 2006-2007, Ext JS, LLC.
106  *
107  * Originally Released Under LGPL - original licence link has changed is not relivant.
108  *
109  * Fork - LGPL
110  * <script type="text/javascript">
111  */
112
113 /**
114 * @class Roo.data.Record
115  * Instances of this class encapsulate both record <em>definition</em> information, and record
116  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
117  * to access Records cached in an {@link Roo.data.Store} object.<br>
118  * <p>
119  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
120  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
121  * objects.<br>
122  * <p>
123  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
124  * @constructor
125  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
126  * {@link #create}. The parameters are the same.
127  * @param {Array} data An associative Array of data values keyed by the field name.
128  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
129  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
130  * not specified an integer id is generated.
131  */
132 Roo.data.Record = function(data, id){
133     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
134     this.data = data;
135 };
136
137 /**
138  * Generate a constructor for a specific record layout.
139  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
140  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
141  * Each field definition object may contain the following properties: <ul>
142  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
143  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
144  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
145  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
146  * is being used, then this is a string containing the javascript expression to reference the data relative to 
147  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
148  * to the data item relative to the record element. If the mapping expression is the same as the field name,
149  * this may be omitted.</p></li>
150  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
151  * <ul><li>auto (Default, implies no conversion)</li>
152  * <li>string</li>
153  * <li>int</li>
154  * <li>float</li>
155  * <li>boolean</li>
156  * <li>date</li></ul></p></li>
157  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
158  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
159  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
160  * by the Reader into an object that will be stored in the Record. It is passed the
161  * following parameters:<ul>
162  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
163  * </ul></p></li>
164  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
165  * </ul>
166  * <br>usage:<br><pre><code>
167 var TopicRecord = Roo.data.Record.create(
168     {name: 'title', mapping: 'topic_title'},
169     {name: 'author', mapping: 'username'},
170     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
171     {name: 'lastPost', mapping: 'post_time', type: 'date'},
172     {name: 'lastPoster', mapping: 'user2'},
173     {name: 'excerpt', mapping: 'post_text'}
174 );
175
176 var myNewRecord = new TopicRecord({
177     title: 'Do my job please',
178     author: 'noobie',
179     totalPosts: 1,
180     lastPost: new Date(),
181     lastPoster: 'Animal',
182     excerpt: 'No way dude!'
183 });
184 myStore.add(myNewRecord);
185 </code></pre>
186  * @method create
187  * @static
188  */
189 Roo.data.Record.create = function(o){
190     var f = function(){
191         f.superclass.constructor.apply(this, arguments);
192     };
193     Roo.extend(f, Roo.data.Record);
194     var p = f.prototype;
195     p.fields = new Roo.util.MixedCollection(false, function(field){
196         return field.name;
197     });
198     for(var i = 0, len = o.length; i < len; i++){
199         p.fields.add(new Roo.data.Field(o[i]));
200     }
201     f.getField = function(name){
202         return p.fields.get(name);  
203     };
204     return f;
205 };
206
207 Roo.data.Record.AUTO_ID = 1000;
208 Roo.data.Record.EDIT = 'edit';
209 Roo.data.Record.REJECT = 'reject';
210 Roo.data.Record.COMMIT = 'commit';
211
212 Roo.data.Record.prototype = {
213     /**
214      * Readonly flag - true if this record has been modified.
215      * @type Boolean
216      */
217     dirty : false,
218     editing : false,
219     error: null,
220     modified: null,
221
222     // private
223     join : function(store){
224         this.store = store;
225     },
226
227     /**
228      * Set the named field to the specified value.
229      * @param {String} name The name of the field to set.
230      * @param {Object} value The value to set the field to.
231      */
232     set : function(name, value){
233         if(this.data[name] == value){
234             return;
235         }
236         this.dirty = true;
237         if(!this.modified){
238             this.modified = {};
239         }
240         if(typeof this.modified[name] == 'undefined'){
241             this.modified[name] = this.data[name];
242         }
243         this.data[name] = value;
244         if(!this.editing && this.store){
245             this.store.afterEdit(this);
246         }       
247     },
248
249     /**
250      * Get the value of the named field.
251      * @param {String} name The name of the field to get the value of.
252      * @return {Object} The value of the field.
253      */
254     get : function(name){
255         return this.data[name]; 
256     },
257
258     // private
259     beginEdit : function(){
260         this.editing = true;
261         this.modified = {}; 
262     },
263
264     // private
265     cancelEdit : function(){
266         this.editing = false;
267         delete this.modified;
268     },
269
270     // private
271     endEdit : function(){
272         this.editing = false;
273         if(this.dirty && this.store){
274             this.store.afterEdit(this);
275         }
276     },
277
278     /**
279      * Usually called by the {@link Roo.data.Store} which owns the Record.
280      * Rejects all changes made to the Record since either creation, or the last commit operation.
281      * Modified fields are reverted to their original values.
282      * <p>
283      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
284      * of reject operations.
285      */
286     reject : function(){
287         var m = this.modified;
288         for(var n in m){
289             if(typeof m[n] != "function"){
290                 this.data[n] = m[n];
291             }
292         }
293         this.dirty = false;
294         delete this.modified;
295         this.editing = false;
296         if(this.store){
297             this.store.afterReject(this);
298         }
299     },
300
301     /**
302      * Usually called by the {@link Roo.data.Store} which owns the Record.
303      * Commits all changes made to the Record since either creation, or the last commit operation.
304      * <p>
305      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
306      * of commit operations.
307      */
308     commit : function(){
309         this.dirty = false;
310         delete this.modified;
311         this.editing = false;
312         if(this.store){
313             this.store.afterCommit(this);
314         }
315     },
316
317     // private
318     hasError : function(){
319         return this.error != null;
320     },
321
322     // private
323     clearError : function(){
324         this.error = null;
325     },
326
327     /**
328      * Creates a copy of this record.
329      * @param {String} id (optional) A new record id if you don't want to use this record's id
330      * @return {Record}
331      */
332     copy : function(newId) {
333         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
334     }
335 };/*
336  * Based on:
337  * Ext JS Library 1.1.1
338  * Copyright(c) 2006-2007, Ext JS, LLC.
339  *
340  * Originally Released Under LGPL - original licence link has changed is not relivant.
341  *
342  * Fork - LGPL
343  * <script type="text/javascript">
344  */
345
346
347
348 /**
349  * @class Roo.data.Store
350  * @extends Roo.util.Observable
351  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
352  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
353  * <p>
354  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
355  * has no knowledge of the format of the data returned by the Proxy.<br>
356  * <p>
357  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
358  * instances from the data object. These records are cached and made available through accessor functions.
359  * @constructor
360  * Creates a new Store.
361  * @param {Object} config A config object containing the objects needed for the Store to access data,
362  * and read the data into Records.
363  */
364 Roo.data.Store = function(config){
365     this.data = new Roo.util.MixedCollection(false);
366     this.data.getKey = function(o){
367         return o.id;
368     };
369     this.baseParams = {};
370     // private
371     this.paramNames = {
372         "start" : "start",
373         "limit" : "limit",
374         "sort" : "sort",
375         "dir" : "dir",
376         "multisort" : "_multisort"
377     };
378
379     if(config && config.data){
380         this.inlineData = config.data;
381         delete config.data;
382     }
383
384     Roo.apply(this, config);
385     
386     if(this.reader){ // reader passed
387         this.reader = Roo.factory(this.reader, Roo.data);
388         this.reader.xmodule = this.xmodule || false;
389         if(!this.recordType){
390             this.recordType = this.reader.recordType;
391         }
392         if(this.reader.onMetaChange){
393             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
394         }
395     }
396
397     if(this.recordType){
398         this.fields = this.recordType.prototype.fields;
399     }
400     this.modified = [];
401
402     this.addEvents({
403         /**
404          * @event datachanged
405          * Fires when the data cache has changed, and a widget which is using this Store
406          * as a Record cache should refresh its view.
407          * @param {Store} this
408          */
409         datachanged : true,
410         /**
411          * @event metachange
412          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
413          * @param {Store} this
414          * @param {Object} meta The JSON metadata
415          */
416         metachange : true,
417         /**
418          * @event add
419          * Fires when Records have been added to the Store
420          * @param {Store} this
421          * @param {Roo.data.Record[]} records The array of Records added
422          * @param {Number} index The index at which the record(s) were added
423          */
424         add : true,
425         /**
426          * @event remove
427          * Fires when a Record has been removed from the Store
428          * @param {Store} this
429          * @param {Roo.data.Record} record The Record that was removed
430          * @param {Number} index The index at which the record was removed
431          */
432         remove : true,
433         /**
434          * @event update
435          * Fires when a Record has been updated
436          * @param {Store} this
437          * @param {Roo.data.Record} record The Record that was updated
438          * @param {String} operation The update operation being performed.  Value may be one of:
439          * <pre><code>
440  Roo.data.Record.EDIT
441  Roo.data.Record.REJECT
442  Roo.data.Record.COMMIT
443          * </code></pre>
444          */
445         update : true,
446         /**
447          * @event clear
448          * Fires when the data cache has been cleared.
449          * @param {Store} this
450          */
451         clear : true,
452         /**
453          * @event beforeload
454          * Fires before a request is made for a new data object.  If the beforeload handler returns false
455          * the load action will be canceled.
456          * @param {Store} this
457          * @param {Object} options The loading options that were specified (see {@link #load} for details)
458          */
459         beforeload : true,
460         /**
461          * @event beforeloadadd
462          * Fires after a new set of Records has been loaded.
463          * @param {Store} this
464          * @param {Roo.data.Record[]} records The Records that were loaded
465          * @param {Object} options The loading options that were specified (see {@link #load} for details)
466          */
467         beforeloadadd : true,
468         /**
469          * @event load
470          * Fires after a new set of Records has been loaded, before they are added to the store.
471          * @param {Store} this
472          * @param {Roo.data.Record[]} records The Records that were loaded
473          * @param {Object} options The loading options that were specified (see {@link #load} for details)
474          * @params {Object} return from reader
475          */
476         load : true,
477         /**
478          * @event loadexception
479          * Fires if an exception occurs in the Proxy during loading.
480          * Called with the signature of the Proxy's "loadexception" event.
481          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
482          * 
483          * @param {Proxy} 
484          * @param {Object} return from JsonData.reader() - success, totalRecords, records
485          * @param {Object} load options 
486          * @param {Object} jsonData from your request (normally this contains the Exception)
487          */
488         loadexception : true
489     });
490     
491     if(this.proxy){
492         this.proxy = Roo.factory(this.proxy, Roo.data);
493         this.proxy.xmodule = this.xmodule || false;
494         this.relayEvents(this.proxy,  ["loadexception"]);
495     }
496     this.sortToggle = {};
497     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
498
499     Roo.data.Store.superclass.constructor.call(this);
500
501     if(this.inlineData){
502         this.loadData(this.inlineData);
503         delete this.inlineData;
504     }
505 };
506
507 Roo.extend(Roo.data.Store, Roo.util.Observable, {
508      /**
509     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
510     * without a remote query - used by combo/forms at present.
511     */
512     
513     /**
514     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
515     */
516     /**
517     * @cfg {Array} data Inline data to be loaded when the store is initialized.
518     */
519     /**
520     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
521     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
522     */
523     /**
524     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
525     * on any HTTP request
526     */
527     /**
528     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
529     */
530     /**
531     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
532     */
533     multiSort: false,
534     /**
535     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
536     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
537     */
538     remoteSort : false,
539
540     /**
541     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
542      * loaded or when a record is removed. (defaults to false).
543     */
544     pruneModifiedRecords : false,
545
546     // private
547     lastOptions : null,
548
549     /**
550      * Add Records to the Store and fires the add event.
551      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
552      */
553     add : function(records){
554         records = [].concat(records);
555         for(var i = 0, len = records.length; i < len; i++){
556             records[i].join(this);
557         }
558         var index = this.data.length;
559         this.data.addAll(records);
560         this.fireEvent("add", this, records, index);
561     },
562
563     /**
564      * Remove a Record from the Store and fires the remove event.
565      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
566      */
567     remove : function(record){
568         var index = this.data.indexOf(record);
569         this.data.removeAt(index);
570  
571         if(this.pruneModifiedRecords){
572             this.modified.remove(record);
573         }
574         this.fireEvent("remove", this, record, index);
575     },
576
577     /**
578      * Remove all Records from the Store and fires the clear event.
579      */
580     removeAll : function(){
581         this.data.clear();
582         if(this.pruneModifiedRecords){
583             this.modified = [];
584         }
585         this.fireEvent("clear", this);
586     },
587
588     /**
589      * Inserts Records to the Store at the given index and fires the add event.
590      * @param {Number} index The start index at which to insert the passed Records.
591      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
592      */
593     insert : function(index, records){
594         records = [].concat(records);
595         for(var i = 0, len = records.length; i < len; i++){
596             this.data.insert(index, records[i]);
597             records[i].join(this);
598         }
599         this.fireEvent("add", this, records, index);
600     },
601
602     /**
603      * Get the index within the cache of the passed Record.
604      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
605      * @return {Number} The index of the passed Record. Returns -1 if not found.
606      */
607     indexOf : function(record){
608         return this.data.indexOf(record);
609     },
610
611     /**
612      * Get the index within the cache of the Record with the passed id.
613      * @param {String} id The id of the Record to find.
614      * @return {Number} The index of the Record. Returns -1 if not found.
615      */
616     indexOfId : function(id){
617         return this.data.indexOfKey(id);
618     },
619
620     /**
621      * Get the Record with the specified id.
622      * @param {String} id The id of the Record to find.
623      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
624      */
625     getById : function(id){
626         return this.data.key(id);
627     },
628
629     /**
630      * Get the Record at the specified index.
631      * @param {Number} index The index of the Record to find.
632      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
633      */
634     getAt : function(index){
635         return this.data.itemAt(index);
636     },
637
638     /**
639      * Returns a range of Records between specified indices.
640      * @param {Number} startIndex (optional) The starting index (defaults to 0)
641      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
642      * @return {Roo.data.Record[]} An array of Records
643      */
644     getRange : function(start, end){
645         return this.data.getRange(start, end);
646     },
647
648     // private
649     storeOptions : function(o){
650         o = Roo.apply({}, o);
651         delete o.callback;
652         delete o.scope;
653         this.lastOptions = o;
654     },
655
656     /**
657      * Loads the Record cache from the configured Proxy using the configured Reader.
658      * <p>
659      * If using remote paging, then the first load call must specify the <em>start</em>
660      * and <em>limit</em> properties in the options.params property to establish the initial
661      * position within the dataset, and the number of Records to cache on each read from the Proxy.
662      * <p>
663      * <strong>It is important to note that for remote data sources, loading is asynchronous,
664      * and this call will return before the new data has been loaded. Perform any post-processing
665      * in a callback function, or in a "load" event handler.</strong>
666      * <p>
667      * @param {Object} options An object containing properties which control loading options:<ul>
668      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
669      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
670      * passed the following arguments:<ul>
671      * <li>r : Roo.data.Record[]</li>
672      * <li>options: Options object from the load call</li>
673      * <li>success: Boolean success indicator</li></ul></li>
674      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
675      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
676      * </ul>
677      */
678     load : function(options){
679         options = options || {};
680         if(this.fireEvent("beforeload", this, options) !== false){
681             this.storeOptions(options);
682             var p = Roo.apply(options.params || {}, this.baseParams);
683             // if meta was not loaded from remote source.. try requesting it.
684             if (!this.reader.metaFromRemote) {
685                 p._requestMeta = 1;
686             }
687             if(this.sortInfo && this.remoteSort){
688                 var pn = this.paramNames;
689                 p[pn["sort"]] = this.sortInfo.field;
690                 p[pn["dir"]] = this.sortInfo.direction;
691             }
692             if (this.multiSort) {
693                 var pn = this.paramNames;
694                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
695             }
696             
697             this.proxy.load(p, this.reader, this.loadRecords, this, options);
698         }
699     },
700
701     /**
702      * Reloads the Record cache from the configured Proxy using the configured Reader and
703      * the options from the last load operation performed.
704      * @param {Object} options (optional) An object containing properties which may override the options
705      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
706      * the most recently used options are reused).
707      */
708     reload : function(options){
709         this.load(Roo.applyIf(options||{}, this.lastOptions));
710     },
711
712     // private
713     // Called as a callback by the Reader during a load operation.
714     loadRecords : function(o, options, success){
715         if(!o || success === false){
716             if(success !== false){
717                 this.fireEvent("load", this, [], options, o);
718             }
719             if(options.callback){
720                 options.callback.call(options.scope || this, [], options, false);
721             }
722             return;
723         }
724         // if data returned failure - throw an exception.
725         if (o.success === false) {
726             // show a message if no listener is registered.
727             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
728                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
729             }
730             // loadmask wil be hooked into this..
731             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
732             return;
733         }
734         var r = o.records, t = o.totalRecords || r.length;
735         
736         this.fireEvent("beforeloadadd", this, r, options, o);
737         
738         if(!options || options.add !== true){
739             if(this.pruneModifiedRecords){
740                 this.modified = [];
741             }
742             for(var i = 0, len = r.length; i < len; i++){
743                 r[i].join(this);
744             }
745             if(this.snapshot){
746                 this.data = this.snapshot;
747                 delete this.snapshot;
748             }
749             this.data.clear();
750             this.data.addAll(r);
751             this.totalLength = t;
752             this.applySort();
753             this.fireEvent("datachanged", this);
754         }else{
755             this.totalLength = Math.max(t, this.data.length+r.length);
756             this.add(r);
757         }
758         
759         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
760                 
761             var e = new Roo.data.Record({});
762
763             e.set(this.parent.displayField, this.parent.emptyTitle);
764             e.set(this.parent.valueField, '');
765
766             this.insert(0, e);
767         }
768             
769         this.fireEvent("load", this, r, options, o);
770         if(options.callback){
771             options.callback.call(options.scope || this, r, options, true);
772         }
773     },
774
775
776     /**
777      * Loads data from a passed data block. A Reader which understands the format of the data
778      * must have been configured in the constructor.
779      * @param {Object} data The data block from which to read the Records.  The format of the data expected
780      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
781      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
782      */
783     loadData : function(o, append){
784         var r = this.reader.readRecords(o);
785         this.loadRecords(r, {add: append}, true);
786     },
787
788     /**
789      * Gets the number of cached records.
790      * <p>
791      * <em>If using paging, this may not be the total size of the dataset. If the data object
792      * used by the Reader contains the dataset size, then the getTotalCount() function returns
793      * the data set size</em>
794      */
795     getCount : function(){
796         return this.data.length || 0;
797     },
798
799     /**
800      * Gets the total number of records in the dataset as returned by the server.
801      * <p>
802      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
803      * the dataset size</em>
804      */
805     getTotalCount : function(){
806         return this.totalLength || 0;
807     },
808
809     /**
810      * Returns the sort state of the Store as an object with two properties:
811      * <pre><code>
812  field {String} The name of the field by which the Records are sorted
813  direction {String} The sort order, "ASC" or "DESC"
814      * </code></pre>
815      */
816     getSortState : function(){
817         return this.sortInfo;
818     },
819
820     // private
821     applySort : function(){
822         if(this.sortInfo && !this.remoteSort){
823             var s = this.sortInfo, f = s.field;
824             var st = this.fields.get(f).sortType;
825             var fn = function(r1, r2){
826                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
827                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
828             };
829             this.data.sort(s.direction, fn);
830             if(this.snapshot && this.snapshot != this.data){
831                 this.snapshot.sort(s.direction, fn);
832             }
833         }
834     },
835
836     /**
837      * Sets the default sort column and order to be used by the next load operation.
838      * @param {String} fieldName The name of the field to sort by.
839      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
840      */
841     setDefaultSort : function(field, dir){
842         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
843     },
844
845     /**
846      * Sort the Records.
847      * If remote sorting is used, the sort is performed on the server, and the cache is
848      * reloaded. If local sorting is used, the cache is sorted internally.
849      * @param {String} fieldName The name of the field to sort by.
850      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
851      */
852     sort : function(fieldName, dir){
853         var f = this.fields.get(fieldName);
854         if(!dir){
855             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
856             
857             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
858                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
859             }else{
860                 dir = f.sortDir;
861             }
862         }
863         this.sortToggle[f.name] = dir;
864         this.sortInfo = {field: f.name, direction: dir};
865         if(!this.remoteSort){
866             this.applySort();
867             this.fireEvent("datachanged", this);
868         }else{
869             this.load(this.lastOptions);
870         }
871     },
872
873     /**
874      * Calls the specified function for each of the Records in the cache.
875      * @param {Function} fn The function to call. The Record is passed as the first parameter.
876      * Returning <em>false</em> aborts and exits the iteration.
877      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
878      */
879     each : function(fn, scope){
880         this.data.each(fn, scope);
881     },
882
883     /**
884      * Gets all records modified since the last commit.  Modified records are persisted across load operations
885      * (e.g., during paging).
886      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
887      */
888     getModifiedRecords : function(){
889         return this.modified;
890     },
891
892     // private
893     createFilterFn : function(property, value, anyMatch){
894         if(!value.exec){ // not a regex
895             value = String(value);
896             if(value.length == 0){
897                 return false;
898             }
899             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
900         }
901         return function(r){
902             return value.test(r.data[property]);
903         };
904     },
905
906     /**
907      * Sums the value of <i>property</i> for each record between start and end and returns the result.
908      * @param {String} property A field on your records
909      * @param {Number} start The record index to start at (defaults to 0)
910      * @param {Number} end The last record index to include (defaults to length - 1)
911      * @return {Number} The sum
912      */
913     sum : function(property, start, end){
914         var rs = this.data.items, v = 0;
915         start = start || 0;
916         end = (end || end === 0) ? end : rs.length-1;
917
918         for(var i = start; i <= end; i++){
919             v += (rs[i].data[property] || 0);
920         }
921         return v;
922     },
923
924     /**
925      * Filter the records by a specified property.
926      * @param {String} field A field on your records
927      * @param {String/RegExp} value Either a string that the field
928      * should start with or a RegExp to test against the field
929      * @param {Boolean} anyMatch True to match any part not just the beginning
930      */
931     filter : function(property, value, anyMatch){
932         var fn = this.createFilterFn(property, value, anyMatch);
933         return fn ? this.filterBy(fn) : this.clearFilter();
934     },
935
936     /**
937      * Filter by a function. The specified function will be called with each
938      * record in this data source. If the function returns true the record is included,
939      * otherwise it is filtered.
940      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
941      * @param {Object} scope (optional) The scope of the function (defaults to this)
942      */
943     filterBy : function(fn, scope){
944         this.snapshot = this.snapshot || this.data;
945         this.data = this.queryBy(fn, scope||this);
946         this.fireEvent("datachanged", this);
947     },
948
949     /**
950      * Query the records by a specified property.
951      * @param {String} field A field on your records
952      * @param {String/RegExp} value Either a string that the field
953      * should start with or a RegExp to test against the field
954      * @param {Boolean} anyMatch True to match any part not just the beginning
955      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
956      */
957     query : function(property, value, anyMatch){
958         var fn = this.createFilterFn(property, value, anyMatch);
959         return fn ? this.queryBy(fn) : this.data.clone();
960     },
961
962     /**
963      * Query by a function. The specified function will be called with each
964      * record in this data source. If the function returns true the record is included
965      * in the results.
966      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
967      * @param {Object} scope (optional) The scope of the function (defaults to this)
968       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
969      **/
970     queryBy : function(fn, scope){
971         var data = this.snapshot || this.data;
972         return data.filterBy(fn, scope||this);
973     },
974
975     /**
976      * Collects unique values for a particular dataIndex from this store.
977      * @param {String} dataIndex The property to collect
978      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
979      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
980      * @return {Array} An array of the unique values
981      **/
982     collect : function(dataIndex, allowNull, bypassFilter){
983         var d = (bypassFilter === true && this.snapshot) ?
984                 this.snapshot.items : this.data.items;
985         var v, sv, r = [], l = {};
986         for(var i = 0, len = d.length; i < len; i++){
987             v = d[i].data[dataIndex];
988             sv = String(v);
989             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
990                 l[sv] = true;
991                 r[r.length] = v;
992             }
993         }
994         return r;
995     },
996
997     /**
998      * Revert to a view of the Record cache with no filtering applied.
999      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
1000      */
1001     clearFilter : function(suppressEvent){
1002         if(this.snapshot && this.snapshot != this.data){
1003             this.data = this.snapshot;
1004             delete this.snapshot;
1005             if(suppressEvent !== true){
1006                 this.fireEvent("datachanged", this);
1007             }
1008         }
1009     },
1010
1011     // private
1012     afterEdit : function(record){
1013         if(this.modified.indexOf(record) == -1){
1014             this.modified.push(record);
1015         }
1016         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
1017     },
1018     
1019     // private
1020     afterReject : function(record){
1021         this.modified.remove(record);
1022         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
1023     },
1024
1025     // private
1026     afterCommit : function(record){
1027         this.modified.remove(record);
1028         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
1029     },
1030
1031     /**
1032      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
1033      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
1034      */
1035     commitChanges : function(){
1036         var m = this.modified.slice(0);
1037         this.modified = [];
1038         for(var i = 0, len = m.length; i < len; i++){
1039             m[i].commit();
1040         }
1041     },
1042
1043     /**
1044      * Cancel outstanding changes on all changed records.
1045      */
1046     rejectChanges : function(){
1047         var m = this.modified.slice(0);
1048         this.modified = [];
1049         for(var i = 0, len = m.length; i < len; i++){
1050             m[i].reject();
1051         }
1052     },
1053
1054     onMetaChange : function(meta, rtype, o){
1055         this.recordType = rtype;
1056         this.fields = rtype.prototype.fields;
1057         delete this.snapshot;
1058         this.sortInfo = meta.sortInfo || this.sortInfo;
1059         this.modified = [];
1060         this.fireEvent('metachange', this, this.reader.meta);
1061     },
1062     
1063     moveIndex : function(data, type)
1064     {
1065         var index = this.indexOf(data);
1066         
1067         var newIndex = index + type;
1068         
1069         this.remove(data);
1070         
1071         this.insert(newIndex, data);
1072         
1073     }
1074 });/*
1075  * Based on:
1076  * Ext JS Library 1.1.1
1077  * Copyright(c) 2006-2007, Ext JS, LLC.
1078  *
1079  * Originally Released Under LGPL - original licence link has changed is not relivant.
1080  *
1081  * Fork - LGPL
1082  * <script type="text/javascript">
1083  */
1084
1085 /**
1086  * @class Roo.data.SimpleStore
1087  * @extends Roo.data.Store
1088  * Small helper class to make creating Stores from Array data easier.
1089  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
1090  * @cfg {Array} fields An array of field definition objects, or field name strings.
1091  * @cfg {Array} data The multi-dimensional array of data
1092  * @constructor
1093  * @param {Object} config
1094  */
1095 Roo.data.SimpleStore = function(config){
1096     Roo.data.SimpleStore.superclass.constructor.call(this, {
1097         isLocal : true,
1098         reader: new Roo.data.ArrayReader({
1099                 id: config.id
1100             },
1101             Roo.data.Record.create(config.fields)
1102         ),
1103         proxy : new Roo.data.MemoryProxy(config.data)
1104     });
1105     this.load();
1106 };
1107 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
1108  * Based on:
1109  * Ext JS Library 1.1.1
1110  * Copyright(c) 2006-2007, Ext JS, LLC.
1111  *
1112  * Originally Released Under LGPL - original licence link has changed is not relivant.
1113  *
1114  * Fork - LGPL
1115  * <script type="text/javascript">
1116  */
1117
1118 /**
1119 /**
1120  * @extends Roo.data.Store
1121  * @class Roo.data.JsonStore
1122  * Small helper class to make creating Stores for JSON data easier. <br/>
1123 <pre><code>
1124 var store = new Roo.data.JsonStore({
1125     url: 'get-images.php',
1126     root: 'images',
1127     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
1128 });
1129 </code></pre>
1130  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
1131  * JsonReader and HttpProxy (unless inline data is provided).</b>
1132  * @cfg {Array} fields An array of field definition objects, or field name strings.
1133  * @constructor
1134  * @param {Object} config
1135  */
1136 Roo.data.JsonStore = function(c){
1137     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
1138         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
1139         reader: new Roo.data.JsonReader(c, c.fields)
1140     }));
1141 };
1142 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
1143  * Based on:
1144  * Ext JS Library 1.1.1
1145  * Copyright(c) 2006-2007, Ext JS, LLC.
1146  *
1147  * Originally Released Under LGPL - original licence link has changed is not relivant.
1148  *
1149  * Fork - LGPL
1150  * <script type="text/javascript">
1151  */
1152
1153  
1154 Roo.data.Field = function(config){
1155     if(typeof config == "string"){
1156         config = {name: config};
1157     }
1158     Roo.apply(this, config);
1159     
1160     if(!this.type){
1161         this.type = "auto";
1162     }
1163     
1164     var st = Roo.data.SortTypes;
1165     // named sortTypes are supported, here we look them up
1166     if(typeof this.sortType == "string"){
1167         this.sortType = st[this.sortType];
1168     }
1169     
1170     // set default sortType for strings and dates
1171     if(!this.sortType){
1172         switch(this.type){
1173             case "string":
1174                 this.sortType = st.asUCString;
1175                 break;
1176             case "date":
1177                 this.sortType = st.asDate;
1178                 break;
1179             default:
1180                 this.sortType = st.none;
1181         }
1182     }
1183
1184     // define once
1185     var stripRe = /[\$,%]/g;
1186
1187     // prebuilt conversion function for this field, instead of
1188     // switching every time we're reading a value
1189     if(!this.convert){
1190         var cv, dateFormat = this.dateFormat;
1191         switch(this.type){
1192             case "":
1193             case "auto":
1194             case undefined:
1195                 cv = function(v){ return v; };
1196                 break;
1197             case "string":
1198                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
1199                 break;
1200             case "int":
1201                 cv = function(v){
1202                     return v !== undefined && v !== null && v !== '' ?
1203                            parseInt(String(v).replace(stripRe, ""), 10) : '';
1204                     };
1205                 break;
1206             case "float":
1207                 cv = function(v){
1208                     return v !== undefined && v !== null && v !== '' ?
1209                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
1210                     };
1211                 break;
1212             case "bool":
1213             case "boolean":
1214                 cv = function(v){ return v === true || v === "true" || v == 1; };
1215                 break;
1216             case "date":
1217                 cv = function(v){
1218                     if(!v){
1219                         return '';
1220                     }
1221                     if(v instanceof Date){
1222                         return v;
1223                     }
1224                     if(dateFormat){
1225                         if(dateFormat == "timestamp"){
1226                             return new Date(v*1000);
1227                         }
1228                         return Date.parseDate(v, dateFormat);
1229                     }
1230                     var parsed = Date.parse(v);
1231                     return parsed ? new Date(parsed) : null;
1232                 };
1233              break;
1234             
1235         }
1236         this.convert = cv;
1237     }
1238 };
1239
1240 Roo.data.Field.prototype = {
1241     dateFormat: null,
1242     defaultValue: "",
1243     mapping: null,
1244     sortType : null,
1245     sortDir : "ASC"
1246 };/*
1247  * Based on:
1248  * Ext JS Library 1.1.1
1249  * Copyright(c) 2006-2007, Ext JS, LLC.
1250  *
1251  * Originally Released Under LGPL - original licence link has changed is not relivant.
1252  *
1253  * Fork - LGPL
1254  * <script type="text/javascript">
1255  */
1256  
1257 // Base class for reading structured data from a data source.  This class is intended to be
1258 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
1259
1260 /**
1261  * @class Roo.data.DataReader
1262  * Base class for reading structured data from a data source.  This class is intended to be
1263  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
1264  */
1265
1266 Roo.data.DataReader = function(meta, recordType){
1267     
1268     this.meta = meta;
1269     
1270     this.recordType = recordType instanceof Array ? 
1271         Roo.data.Record.create(recordType) : recordType;
1272 };
1273
1274 Roo.data.DataReader.prototype = {
1275      /**
1276      * Create an empty record
1277      * @param {Object} data (optional) - overlay some values
1278      * @return {Roo.data.Record} record created.
1279      */
1280     newRow :  function(d) {
1281         var da =  {};
1282         this.recordType.prototype.fields.each(function(c) {
1283             switch( c.type) {
1284                 case 'int' : da[c.name] = 0; break;
1285                 case 'date' : da[c.name] = new Date(); break;
1286                 case 'float' : da[c.name] = 0.0; break;
1287                 case 'boolean' : da[c.name] = false; break;
1288                 default : da[c.name] = ""; break;
1289             }
1290             
1291         });
1292         return new this.recordType(Roo.apply(da, d));
1293     }
1294     
1295 };/*
1296  * Based on:
1297  * Ext JS Library 1.1.1
1298  * Copyright(c) 2006-2007, Ext JS, LLC.
1299  *
1300  * Originally Released Under LGPL - original licence link has changed is not relivant.
1301  *
1302  * Fork - LGPL
1303  * <script type="text/javascript">
1304  */
1305
1306 /**
1307  * @class Roo.data.DataProxy
1308  * @extends Roo.data.Observable
1309  * This class is an abstract base class for implementations which provide retrieval of
1310  * unformatted data objects.<br>
1311  * <p>
1312  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
1313  * (of the appropriate type which knows how to parse the data object) to provide a block of
1314  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
1315  * <p>
1316  * Custom implementations must implement the load method as described in
1317  * {@link Roo.data.HttpProxy#load}.
1318  */
1319 Roo.data.DataProxy = function(){
1320     this.addEvents({
1321         /**
1322          * @event beforeload
1323          * Fires before a network request is made to retrieve a data object.
1324          * @param {Object} This DataProxy object.
1325          * @param {Object} params The params parameter to the load function.
1326          */
1327         beforeload : true,
1328         /**
1329          * @event load
1330          * Fires before the load method's callback is called.
1331          * @param {Object} This DataProxy object.
1332          * @param {Object} o The data object.
1333          * @param {Object} arg The callback argument object passed to the load function.
1334          */
1335         load : true,
1336         /**
1337          * @event loadexception
1338          * Fires if an Exception occurs during data retrieval.
1339          * @param {Object} This DataProxy object.
1340          * @param {Object} o The data object.
1341          * @param {Object} arg The callback argument object passed to the load function.
1342          * @param {Object} e The Exception.
1343          */
1344         loadexception : true
1345     });
1346     Roo.data.DataProxy.superclass.constructor.call(this);
1347 };
1348
1349 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
1350
1351     /**
1352      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
1353      */
1354 /*
1355  * Based on:
1356  * Ext JS Library 1.1.1
1357  * Copyright(c) 2006-2007, Ext JS, LLC.
1358  *
1359  * Originally Released Under LGPL - original licence link has changed is not relivant.
1360  *
1361  * Fork - LGPL
1362  * <script type="text/javascript">
1363  */
1364 /**
1365  * @class Roo.data.MemoryProxy
1366  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
1367  * to the Reader when its load method is called.
1368  * @constructor
1369  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
1370  */
1371 Roo.data.MemoryProxy = function(data){
1372     if (data.data) {
1373         data = data.data;
1374     }
1375     Roo.data.MemoryProxy.superclass.constructor.call(this);
1376     this.data = data;
1377 };
1378
1379 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
1380     
1381     /**
1382      * Load data from the requested source (in this case an in-memory
1383      * data object passed to the constructor), read the data object into
1384      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1385      * process that block using the passed callback.
1386      * @param {Object} params This parameter is not used by the MemoryProxy class.
1387      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1388      * object into a block of Roo.data.Records.
1389      * @param {Function} callback The function into which to pass the block of Roo.data.records.
1390      * The function must be passed <ul>
1391      * <li>The Record block object</li>
1392      * <li>The "arg" argument from the load function</li>
1393      * <li>A boolean success indicator</li>
1394      * </ul>
1395      * @param {Object} scope The scope in which to call the callback
1396      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1397      */
1398     load : function(params, reader, callback, scope, arg){
1399         params = params || {};
1400         var result;
1401         try {
1402             result = reader.readRecords(params.data ? params.data :this.data);
1403         }catch(e){
1404             this.fireEvent("loadexception", this, arg, null, e);
1405             callback.call(scope, null, arg, false);
1406             return;
1407         }
1408         callback.call(scope, result, arg, true);
1409     },
1410     
1411     // private
1412     update : function(params, records){
1413         
1414     }
1415 });/*
1416  * Based on:
1417  * Ext JS Library 1.1.1
1418  * Copyright(c) 2006-2007, Ext JS, LLC.
1419  *
1420  * Originally Released Under LGPL - original licence link has changed is not relivant.
1421  *
1422  * Fork - LGPL
1423  * <script type="text/javascript">
1424  */
1425 /**
1426  * @class Roo.data.HttpProxy
1427  * @extends Roo.data.DataProxy
1428  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
1429  * configured to reference a certain URL.<br><br>
1430  * <p>
1431  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
1432  * from which the running page was served.<br><br>
1433  * <p>
1434  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
1435  * <p>
1436  * Be aware that to enable the browser to parse an XML document, the server must set
1437  * the Content-Type header in the HTTP response to "text/xml".
1438  * @constructor
1439  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
1440  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
1441  * will be used to make the request.
1442  */
1443 Roo.data.HttpProxy = function(conn){
1444     Roo.data.HttpProxy.superclass.constructor.call(this);
1445     // is conn a conn config or a real conn?
1446     this.conn = conn;
1447     this.useAjax = !conn || !conn.events;
1448   
1449 };
1450
1451 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
1452     // thse are take from connection...
1453     
1454     /**
1455      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
1456      */
1457     /**
1458      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
1459      * extra parameters to each request made by this object. (defaults to undefined)
1460      */
1461     /**
1462      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
1463      *  to each request made by this object. (defaults to undefined)
1464      */
1465     /**
1466      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
1467      */
1468     /**
1469      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
1470      */
1471      /**
1472      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
1473      * @type Boolean
1474      */
1475   
1476
1477     /**
1478      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
1479      * @type Boolean
1480      */
1481     /**
1482      * Return the {@link Roo.data.Connection} object being used by this Proxy.
1483      * @return {Connection} The Connection object. This object may be used to subscribe to events on
1484      * a finer-grained basis than the DataProxy events.
1485      */
1486     getConnection : function(){
1487         return this.useAjax ? Roo.Ajax : this.conn;
1488     },
1489
1490     /**
1491      * Load data from the configured {@link Roo.data.Connection}, read the data object into
1492      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
1493      * process that block using the passed callback.
1494      * @param {Object} params An object containing properties which are to be used as HTTP parameters
1495      * for the request to the remote server.
1496      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1497      * object into a block of Roo.data.Records.
1498      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1499      * The function must be passed <ul>
1500      * <li>The Record block object</li>
1501      * <li>The "arg" argument from the load function</li>
1502      * <li>A boolean success indicator</li>
1503      * </ul>
1504      * @param {Object} scope The scope in which to call the callback
1505      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1506      */
1507     load : function(params, reader, callback, scope, arg){
1508         if(this.fireEvent("beforeload", this, params) !== false){
1509             var  o = {
1510                 params : params || {},
1511                 request: {
1512                     callback : callback,
1513                     scope : scope,
1514                     arg : arg
1515                 },
1516                 reader: reader,
1517                 callback : this.loadResponse,
1518                 scope: this
1519             };
1520             if(this.useAjax){
1521                 Roo.applyIf(o, this.conn);
1522                 if(this.activeRequest){
1523                     Roo.Ajax.abort(this.activeRequest);
1524                 }
1525                 this.activeRequest = Roo.Ajax.request(o);
1526             }else{
1527                 this.conn.request(o);
1528             }
1529         }else{
1530             callback.call(scope||this, null, arg, false);
1531         }
1532     },
1533
1534     // private
1535     loadResponse : function(o, success, response){
1536         delete this.activeRequest;
1537         if(!success){
1538             this.fireEvent("loadexception", this, o, response);
1539             o.request.callback.call(o.request.scope, null, o.request.arg, false);
1540             return;
1541         }
1542         var result;
1543         try {
1544             result = o.reader.read(response);
1545         }catch(e){
1546             this.fireEvent("loadexception", this, o, response, e);
1547             o.request.callback.call(o.request.scope, null, o.request.arg, false);
1548             return;
1549         }
1550         
1551         this.fireEvent("load", this, o, o.request.arg);
1552         o.request.callback.call(o.request.scope, result, o.request.arg, true);
1553     },
1554
1555     // private
1556     update : function(dataSet){
1557
1558     },
1559
1560     // private
1561     updateResponse : function(dataSet){
1562
1563     }
1564 });/*
1565  * Based on:
1566  * Ext JS Library 1.1.1
1567  * Copyright(c) 2006-2007, Ext JS, LLC.
1568  *
1569  * Originally Released Under LGPL - original licence link has changed is not relivant.
1570  *
1571  * Fork - LGPL
1572  * <script type="text/javascript">
1573  */
1574
1575 /**
1576  * @class Roo.data.ScriptTagProxy
1577  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
1578  * other than the originating domain of the running page.<br><br>
1579  * <p>
1580  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
1581  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
1582  * <p>
1583  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
1584  * source code that is used as the source inside a &lt;script> tag.<br><br>
1585  * <p>
1586  * In order for the browser to process the returned data, the server must wrap the data object
1587  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
1588  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
1589  * depending on whether the callback name was passed:
1590  * <p>
1591  * <pre><code>
1592 boolean scriptTag = false;
1593 String cb = request.getParameter("callback");
1594 if (cb != null) {
1595     scriptTag = true;
1596     response.setContentType("text/javascript");
1597 } else {
1598     response.setContentType("application/x-json");
1599 }
1600 Writer out = response.getWriter();
1601 if (scriptTag) {
1602     out.write(cb + "(");
1603 }
1604 out.print(dataBlock.toJsonString());
1605 if (scriptTag) {
1606     out.write(");");
1607 }
1608 </pre></code>
1609  *
1610  * @constructor
1611  * @param {Object} config A configuration object.
1612  */
1613 Roo.data.ScriptTagProxy = function(config){
1614     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
1615     Roo.apply(this, config);
1616     this.head = document.getElementsByTagName("head")[0];
1617 };
1618
1619 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
1620
1621 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
1622     /**
1623      * @cfg {String} url The URL from which to request the data object.
1624      */
1625     /**
1626      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
1627      */
1628     timeout : 30000,
1629     /**
1630      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
1631      * the server the name of the callback function set up by the load call to process the returned data object.
1632      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
1633      * javascript output which calls this named function passing the data object as its only parameter.
1634      */
1635     callbackParam : "callback",
1636     /**
1637      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
1638      * name to the request.
1639      */
1640     nocache : true,
1641
1642     /**
1643      * Load data from the configured URL, read the data object into
1644      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1645      * process that block using the passed callback.
1646      * @param {Object} params An object containing properties which are to be used as HTTP parameters
1647      * for the request to the remote server.
1648      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1649      * object into a block of Roo.data.Records.
1650      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1651      * The function must be passed <ul>
1652      * <li>The Record block object</li>
1653      * <li>The "arg" argument from the load function</li>
1654      * <li>A boolean success indicator</li>
1655      * </ul>
1656      * @param {Object} scope The scope in which to call the callback
1657      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1658      */
1659     load : function(params, reader, callback, scope, arg){
1660         if(this.fireEvent("beforeload", this, params) !== false){
1661
1662             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
1663
1664             var url = this.url;
1665             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
1666             if(this.nocache){
1667                 url += "&_dc=" + (new Date().getTime());
1668             }
1669             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
1670             var trans = {
1671                 id : transId,
1672                 cb : "stcCallback"+transId,
1673                 scriptId : "stcScript"+transId,
1674                 params : params,
1675                 arg : arg,
1676                 url : url,
1677                 callback : callback,
1678                 scope : scope,
1679                 reader : reader
1680             };
1681             var conn = this;
1682
1683             window[trans.cb] = function(o){
1684                 conn.handleResponse(o, trans);
1685             };
1686
1687             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
1688
1689             if(this.autoAbort !== false){
1690                 this.abort();
1691             }
1692
1693             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
1694
1695             var script = document.createElement("script");
1696             script.setAttribute("src", url);
1697             script.setAttribute("type", "text/javascript");
1698             script.setAttribute("id", trans.scriptId);
1699             this.head.appendChild(script);
1700
1701             this.trans = trans;
1702         }else{
1703             callback.call(scope||this, null, arg, false);
1704         }
1705     },
1706
1707     // private
1708     isLoading : function(){
1709         return this.trans ? true : false;
1710     },
1711
1712     /**
1713      * Abort the current server request.
1714      */
1715     abort : function(){
1716         if(this.isLoading()){
1717             this.destroyTrans(this.trans);
1718         }
1719     },
1720
1721     // private
1722     destroyTrans : function(trans, isLoaded){
1723         this.head.removeChild(document.getElementById(trans.scriptId));
1724         clearTimeout(trans.timeoutId);
1725         if(isLoaded){
1726             window[trans.cb] = undefined;
1727             try{
1728                 delete window[trans.cb];
1729             }catch(e){}
1730         }else{
1731             // if hasn't been loaded, wait for load to remove it to prevent script error
1732             window[trans.cb] = function(){
1733                 window[trans.cb] = undefined;
1734                 try{
1735                     delete window[trans.cb];
1736                 }catch(e){}
1737             };
1738         }
1739     },
1740
1741     // private
1742     handleResponse : function(o, trans){
1743         this.trans = false;
1744         this.destroyTrans(trans, true);
1745         var result;
1746         try {
1747             result = trans.reader.readRecords(o);
1748         }catch(e){
1749             this.fireEvent("loadexception", this, o, trans.arg, e);
1750             trans.callback.call(trans.scope||window, null, trans.arg, false);
1751             return;
1752         }
1753         this.fireEvent("load", this, o, trans.arg);
1754         trans.callback.call(trans.scope||window, result, trans.arg, true);
1755     },
1756
1757     // private
1758     handleFailure : function(trans){
1759         this.trans = false;
1760         this.destroyTrans(trans, false);
1761         this.fireEvent("loadexception", this, null, trans.arg);
1762         trans.callback.call(trans.scope||window, null, trans.arg, false);
1763     }
1764 });/*
1765  * Based on:
1766  * Ext JS Library 1.1.1
1767  * Copyright(c) 2006-2007, Ext JS, LLC.
1768  *
1769  * Originally Released Under LGPL - original licence link has changed is not relivant.
1770  *
1771  * Fork - LGPL
1772  * <script type="text/javascript">
1773  */
1774
1775 /**
1776  * @class Roo.data.JsonReader
1777  * @extends Roo.data.DataReader
1778  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
1779  * based on mappings in a provided Roo.data.Record constructor.
1780  * 
1781  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
1782  * in the reply previously. 
1783  * 
1784  * <p>
1785  * Example code:
1786  * <pre><code>
1787 var RecordDef = Roo.data.Record.create([
1788     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
1789     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
1790 ]);
1791 var myReader = new Roo.data.JsonReader({
1792     totalProperty: "results",    // The property which contains the total dataset size (optional)
1793     root: "rows",                // The property which contains an Array of row objects
1794     id: "id"                     // The property within each row object that provides an ID for the record (optional)
1795 }, RecordDef);
1796 </code></pre>
1797  * <p>
1798  * This would consume a JSON file like this:
1799  * <pre><code>
1800 { 'results': 2, 'rows': [
1801     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
1802     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
1803 }
1804 </code></pre>
1805  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
1806  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
1807  * paged from the remote server.
1808  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
1809  * @cfg {String} root name of the property which contains the Array of row objects.
1810  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
1811  * @cfg {Array} fields Array of field definition objects
1812  * @constructor
1813  * Create a new JsonReader
1814  * @param {Object} meta Metadata configuration options
1815  * @param {Object} recordType Either an Array of field definition objects,
1816  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
1817  */
1818 Roo.data.JsonReader = function(meta, recordType){
1819     
1820     meta = meta || {};
1821     // set some defaults:
1822     Roo.applyIf(meta, {
1823         totalProperty: 'total',
1824         successProperty : 'success',
1825         root : 'data',
1826         id : 'id'
1827     });
1828     
1829     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
1830 };
1831 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
1832     
1833     /**
1834      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
1835      * Used by Store query builder to append _requestMeta to params.
1836      * 
1837      */
1838     metaFromRemote : false,
1839     /**
1840      * This method is only used by a DataProxy which has retrieved data from a remote server.
1841      * @param {Object} response The XHR object which contains the JSON data in its responseText.
1842      * @return {Object} data A data block which is used by an Roo.data.Store object as
1843      * a cache of Roo.data.Records.
1844      */
1845     read : function(response){
1846         var json = response.responseText;
1847        
1848         var o = /* eval:var:o */ eval("("+json+")");
1849         if(!o) {
1850             throw {message: "JsonReader.read: Json object not found"};
1851         }
1852         
1853         if(o.metaData){
1854             
1855             delete this.ef;
1856             this.metaFromRemote = true;
1857             this.meta = o.metaData;
1858             this.recordType = Roo.data.Record.create(o.metaData.fields);
1859             this.onMetaChange(this.meta, this.recordType, o);
1860         }
1861         return this.readRecords(o);
1862     },
1863
1864     // private function a store will implement
1865     onMetaChange : function(meta, recordType, o){
1866
1867     },
1868
1869     /**
1870          * @ignore
1871          */
1872     simpleAccess: function(obj, subsc) {
1873         return obj[subsc];
1874     },
1875
1876         /**
1877          * @ignore
1878          */
1879     getJsonAccessor: function(){
1880         var re = /[\[\.]/;
1881         return function(expr) {
1882             try {
1883                 return(re.test(expr))
1884                     ? new Function("obj", "return obj." + expr)
1885                     : function(obj){
1886                         return obj[expr];
1887                     };
1888             } catch(e){}
1889             return Roo.emptyFn;
1890         };
1891     }(),
1892
1893     /**
1894      * Create a data block containing Roo.data.Records from an XML document.
1895      * @param {Object} o An object which contains an Array of row objects in the property specified
1896      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
1897      * which contains the total size of the dataset.
1898      * @return {Object} data A data block which is used by an Roo.data.Store object as
1899      * a cache of Roo.data.Records.
1900      */
1901     readRecords : function(o){
1902         /**
1903          * After any data loads, the raw JSON data is available for further custom processing.
1904          * @type Object
1905          */
1906         this.o = o;
1907         var s = this.meta, Record = this.recordType,
1908             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
1909
1910 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
1911         if (!this.ef) {
1912             if(s.totalProperty) {
1913                     this.getTotal = this.getJsonAccessor(s.totalProperty);
1914                 }
1915                 if(s.successProperty) {
1916                     this.getSuccess = this.getJsonAccessor(s.successProperty);
1917                 }
1918                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
1919                 if (s.id) {
1920                         var g = this.getJsonAccessor(s.id);
1921                         this.getId = function(rec) {
1922                                 var r = g(rec);  
1923                                 return (r === undefined || r === "") ? null : r;
1924                         };
1925                 } else {
1926                         this.getId = function(){return null;};
1927                 }
1928             this.ef = [];
1929             for(var jj = 0; jj < fl; jj++){
1930                 f = fi[jj];
1931                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
1932                 this.ef[jj] = this.getJsonAccessor(map);
1933             }
1934         }
1935
1936         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
1937         if(s.totalProperty){
1938             var vt = parseInt(this.getTotal(o), 10);
1939             if(!isNaN(vt)){
1940                 totalRecords = vt;
1941             }
1942         }
1943         if(s.successProperty){
1944             var vs = this.getSuccess(o);
1945             if(vs === false || vs === 'false'){
1946                 success = false;
1947             }
1948         }
1949         var records = [];
1950         for(var i = 0; i < c; i++){
1951                 var n = root[i];
1952             var values = {};
1953             var id = this.getId(n);
1954             for(var j = 0; j < fl; j++){
1955                 f = fi[j];
1956             var v = this.ef[j](n);
1957             if (!f.convert) {
1958                 Roo.log('missing convert for ' + f.name);
1959                 Roo.log(f);
1960                 continue;
1961             }
1962             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
1963             }
1964             var record = new Record(values, id);
1965             record.json = n;
1966             records[i] = record;
1967         }
1968         return {
1969             raw : o,
1970             success : success,
1971             records : records,
1972             totalRecords : totalRecords
1973         };
1974     }
1975 });/*
1976  * Based on:
1977  * Ext JS Library 1.1.1
1978  * Copyright(c) 2006-2007, Ext JS, LLC.
1979  *
1980  * Originally Released Under LGPL - original licence link has changed is not relivant.
1981  *
1982  * Fork - LGPL
1983  * <script type="text/javascript">
1984  */
1985
1986 /**
1987  * @class Roo.data.XmlReader
1988  * @extends Roo.data.DataReader
1989  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
1990  * based on mappings in a provided Roo.data.Record constructor.<br><br>
1991  * <p>
1992  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
1993  * header in the HTTP response must be set to "text/xml".</em>
1994  * <p>
1995  * Example code:
1996  * <pre><code>
1997 var RecordDef = Roo.data.Record.create([
1998    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
1999    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
2000 ]);
2001 var myReader = new Roo.data.XmlReader({
2002    totalRecords: "results", // The element which contains the total dataset size (optional)
2003    record: "row",           // The repeated element which contains row information
2004    id: "id"                 // The element within the row that provides an ID for the record (optional)
2005 }, RecordDef);
2006 </code></pre>
2007  * <p>
2008  * This would consume an XML file like this:
2009  * <pre><code>
2010 &lt;?xml?>
2011 &lt;dataset>
2012  &lt;results>2&lt;/results>
2013  &lt;row>
2014    &lt;id>1&lt;/id>
2015    &lt;name>Bill&lt;/name>
2016    &lt;occupation>Gardener&lt;/occupation>
2017  &lt;/row>
2018  &lt;row>
2019    &lt;id>2&lt;/id>
2020    &lt;name>Ben&lt;/name>
2021    &lt;occupation>Horticulturalist&lt;/occupation>
2022  &lt;/row>
2023 &lt;/dataset>
2024 </code></pre>
2025  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
2026  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
2027  * paged from the remote server.
2028  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
2029  * @cfg {String} success The DomQuery path to the success attribute used by forms.
2030  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
2031  * a record identifier value.
2032  * @constructor
2033  * Create a new XmlReader
2034  * @param {Object} meta Metadata configuration options
2035  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
2036  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
2037  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
2038  */
2039 Roo.data.XmlReader = function(meta, recordType){
2040     meta = meta || {};
2041     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2042 };
2043 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
2044     /**
2045      * This method is only used by a DataProxy which has retrieved data from a remote server.
2046          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
2047          * to contain a method called 'responseXML' that returns an XML document object.
2048      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2049      * a cache of Roo.data.Records.
2050      */
2051     read : function(response){
2052         var doc = response.responseXML;
2053         if(!doc) {
2054             throw {message: "XmlReader.read: XML Document not available"};
2055         }
2056         return this.readRecords(doc);
2057     },
2058
2059     /**
2060      * Create a data block containing Roo.data.Records from an XML document.
2061          * @param {Object} doc A parsed XML document.
2062      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2063      * a cache of Roo.data.Records.
2064      */
2065     readRecords : function(doc){
2066         /**
2067          * After any data loads/reads, the raw XML Document is available for further custom processing.
2068          * @type XMLDocument
2069          */
2070         this.xmlData = doc;
2071         var root = doc.documentElement || doc;
2072         var q = Roo.DomQuery;
2073         var recordType = this.recordType, fields = recordType.prototype.fields;
2074         var sid = this.meta.id;
2075         var totalRecords = 0, success = true;
2076         if(this.meta.totalRecords){
2077             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
2078         }
2079         
2080         if(this.meta.success){
2081             var sv = q.selectValue(this.meta.success, root, true);
2082             success = sv !== false && sv !== 'false';
2083         }
2084         var records = [];
2085         var ns = q.select(this.meta.record, root);
2086         for(var i = 0, len = ns.length; i < len; i++) {
2087                 var n = ns[i];
2088                 var values = {};
2089                 var id = sid ? q.selectValue(sid, n) : undefined;
2090                 for(var j = 0, jlen = fields.length; j < jlen; j++){
2091                     var f = fields.items[j];
2092                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
2093                     v = f.convert(v);
2094                     values[f.name] = v;
2095                 }
2096                 var record = new recordType(values, id);
2097                 record.node = n;
2098                 records[records.length] = record;
2099             }
2100
2101             return {
2102                 success : success,
2103                 records : records,
2104                 totalRecords : totalRecords || records.length
2105             };
2106     }
2107 });/*
2108  * Based on:
2109  * Ext JS Library 1.1.1
2110  * Copyright(c) 2006-2007, Ext JS, LLC.
2111  *
2112  * Originally Released Under LGPL - original licence link has changed is not relivant.
2113  *
2114  * Fork - LGPL
2115  * <script type="text/javascript">
2116  */
2117
2118 /**
2119  * @class Roo.data.ArrayReader
2120  * @extends Roo.data.DataReader
2121  * Data reader class to create an Array of Roo.data.Record objects from an Array.
2122  * Each element of that Array represents a row of data fields. The
2123  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
2124  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
2125  * <p>
2126  * Example code:.
2127  * <pre><code>
2128 var RecordDef = Roo.data.Record.create([
2129     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
2130     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
2131 ]);
2132 var myReader = new Roo.data.ArrayReader({
2133     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
2134 }, RecordDef);
2135 </code></pre>
2136  * <p>
2137  * This would consume an Array like this:
2138  * <pre><code>
2139 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
2140   </code></pre>
2141  
2142  * @constructor
2143  * Create a new JsonReader
2144  * @param {Object} meta Metadata configuration options.
2145  * @param {Object|Array} recordType Either an Array of field definition objects
2146  * 
2147  * @cfg {Array} fields Array of field definition objects
2148  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
2149  * as specified to {@link Roo.data.Record#create},
2150  * or an {@link Roo.data.Record} object
2151  *
2152  * 
2153  * created using {@link Roo.data.Record#create}.
2154  */
2155 Roo.data.ArrayReader = function(meta, recordType){
2156     
2157      
2158     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2159 };
2160
2161 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
2162     /**
2163      * Create a data block containing Roo.data.Records from an XML document.
2164      * @param {Object} o An Array of row objects which represents the dataset.
2165      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
2166      * a cache of Roo.data.Records.
2167      */
2168     readRecords : function(o){
2169         var sid = this.meta ? this.meta.id : null;
2170         var recordType = this.recordType, fields = recordType.prototype.fields;
2171         var records = [];
2172         var root = o;
2173             for(var i = 0; i < root.length; i++){
2174                     var n = root[i];
2175                 var values = {};
2176                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
2177                 for(var j = 0, jlen = fields.length; j < jlen; j++){
2178                 var f = fields.items[j];
2179                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
2180                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
2181                 v = f.convert(v);
2182                 values[f.name] = v;
2183             }
2184                 var record = new recordType(values, id);
2185                 record.json = n;
2186                 records[records.length] = record;
2187             }
2188             return {
2189                 records : records,
2190                 totalRecords : records.length
2191             };
2192     }
2193 });/*
2194  * Based on:
2195  * Ext JS Library 1.1.1
2196  * Copyright(c) 2006-2007, Ext JS, LLC.
2197  *
2198  * Originally Released Under LGPL - original licence link has changed is not relivant.
2199  *
2200  * Fork - LGPL
2201  * <script type="text/javascript">
2202  */
2203
2204
2205 /**
2206  * @class Roo.data.Tree
2207  * @extends Roo.util.Observable
2208  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
2209  * in the tree have most standard DOM functionality.
2210  * @constructor
2211  * @param {Node} root (optional) The root node
2212  */
2213 Roo.data.Tree = function(root){
2214    this.nodeHash = {};
2215    /**
2216     * The root node for this tree
2217     * @type Node
2218     */
2219    this.root = null;
2220    if(root){
2221        this.setRootNode(root);
2222    }
2223    this.addEvents({
2224        /**
2225         * @event append
2226         * Fires when a new child node is appended to a node in this tree.
2227         * @param {Tree} tree The owner tree
2228         * @param {Node} parent The parent node
2229         * @param {Node} node The newly appended node
2230         * @param {Number} index The index of the newly appended node
2231         */
2232        "append" : true,
2233        /**
2234         * @event remove
2235         * Fires when a child node is removed from a node in this tree.
2236         * @param {Tree} tree The owner tree
2237         * @param {Node} parent The parent node
2238         * @param {Node} node The child node removed
2239         */
2240        "remove" : true,
2241        /**
2242         * @event move
2243         * Fires when a node is moved to a new location in the tree
2244         * @param {Tree} tree The owner tree
2245         * @param {Node} node The node moved
2246         * @param {Node} oldParent The old parent of this node
2247         * @param {Node} newParent The new parent of this node
2248         * @param {Number} index The index it was moved to
2249         */
2250        "move" : true,
2251        /**
2252         * @event insert
2253         * Fires when a new child node is inserted in a node in this tree.
2254         * @param {Tree} tree The owner tree
2255         * @param {Node} parent The parent node
2256         * @param {Node} node The child node inserted
2257         * @param {Node} refNode The child node the node was inserted before
2258         */
2259        "insert" : true,
2260        /**
2261         * @event beforeappend
2262         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
2263         * @param {Tree} tree The owner tree
2264         * @param {Node} parent The parent node
2265         * @param {Node} node The child node to be appended
2266         */
2267        "beforeappend" : true,
2268        /**
2269         * @event beforeremove
2270         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
2271         * @param {Tree} tree The owner tree
2272         * @param {Node} parent The parent node
2273         * @param {Node} node The child node to be removed
2274         */
2275        "beforeremove" : true,
2276        /**
2277         * @event beforemove
2278         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
2279         * @param {Tree} tree The owner tree
2280         * @param {Node} node The node being moved
2281         * @param {Node} oldParent The parent of the node
2282         * @param {Node} newParent The new parent the node is moving to
2283         * @param {Number} index The index it is being moved to
2284         */
2285        "beforemove" : true,
2286        /**
2287         * @event beforeinsert
2288         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
2289         * @param {Tree} tree The owner tree
2290         * @param {Node} parent The parent node
2291         * @param {Node} node The child node to be inserted
2292         * @param {Node} refNode The child node the node is being inserted before
2293         */
2294        "beforeinsert" : true
2295    });
2296
2297     Roo.data.Tree.superclass.constructor.call(this);
2298 };
2299
2300 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
2301     pathSeparator: "/",
2302
2303     proxyNodeEvent : function(){
2304         return this.fireEvent.apply(this, arguments);
2305     },
2306
2307     /**
2308      * Returns the root node for this tree.
2309      * @return {Node}
2310      */
2311     getRootNode : function(){
2312         return this.root;
2313     },
2314
2315     /**
2316      * Sets the root node for this tree.
2317      * @param {Node} node
2318      * @return {Node}
2319      */
2320     setRootNode : function(node){
2321         this.root = node;
2322         node.ownerTree = this;
2323         node.isRoot = true;
2324         this.registerNode(node);
2325         return node;
2326     },
2327
2328     /**
2329      * Gets a node in this tree by its id.
2330      * @param {String} id
2331      * @return {Node}
2332      */
2333     getNodeById : function(id){
2334         return this.nodeHash[id];
2335     },
2336
2337     registerNode : function(node){
2338         this.nodeHash[node.id] = node;
2339     },
2340
2341     unregisterNode : function(node){
2342         delete this.nodeHash[node.id];
2343     },
2344
2345     toString : function(){
2346         return "[Tree"+(this.id?" "+this.id:"")+"]";
2347     }
2348 });
2349
2350 /**
2351  * @class Roo.data.Node
2352  * @extends Roo.util.Observable
2353  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
2354  * @cfg {String} id The id for this node. If one is not specified, one is generated.
2355  * @constructor
2356  * @param {Object} attributes The attributes/config for the node
2357  */
2358 Roo.data.Node = function(attributes){
2359     /**
2360      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
2361      * @type {Object}
2362      */
2363     this.attributes = attributes || {};
2364     this.leaf = this.attributes.leaf;
2365     /**
2366      * The node id. @type String
2367      */
2368     this.id = this.attributes.id;
2369     if(!this.id){
2370         this.id = Roo.id(null, "ynode-");
2371         this.attributes.id = this.id;
2372     }
2373      
2374     
2375     /**
2376      * All child nodes of this node. @type Array
2377      */
2378     this.childNodes = [];
2379     if(!this.childNodes.indexOf){ // indexOf is a must
2380         this.childNodes.indexOf = function(o){
2381             for(var i = 0, len = this.length; i < len; i++){
2382                 if(this[i] == o) {
2383                     return i;
2384                 }
2385             }
2386             return -1;
2387         };
2388     }
2389     /**
2390      * The parent node for this node. @type Node
2391      */
2392     this.parentNode = null;
2393     /**
2394      * The first direct child node of this node, or null if this node has no child nodes. @type Node
2395      */
2396     this.firstChild = null;
2397     /**
2398      * The last direct child node of this node, or null if this node has no child nodes. @type Node
2399      */
2400     this.lastChild = null;
2401     /**
2402      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
2403      */
2404     this.previousSibling = null;
2405     /**
2406      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
2407      */
2408     this.nextSibling = null;
2409
2410     this.addEvents({
2411        /**
2412         * @event append
2413         * Fires when a new child node is appended
2414         * @param {Tree} tree The owner tree
2415         * @param {Node} this This node
2416         * @param {Node} node The newly appended node
2417         * @param {Number} index The index of the newly appended node
2418         */
2419        "append" : true,
2420        /**
2421         * @event remove
2422         * Fires when a child node is removed
2423         * @param {Tree} tree The owner tree
2424         * @param {Node} this This node
2425         * @param {Node} node The removed node
2426         */
2427        "remove" : true,
2428        /**
2429         * @event move
2430         * Fires when this node is moved to a new location in the tree
2431         * @param {Tree} tree The owner tree
2432         * @param {Node} this This node
2433         * @param {Node} oldParent The old parent of this node
2434         * @param {Node} newParent The new parent of this node
2435         * @param {Number} index The index it was moved to
2436         */
2437        "move" : true,
2438        /**
2439         * @event insert
2440         * Fires when a new child node is inserted.
2441         * @param {Tree} tree The owner tree
2442         * @param {Node} this This node
2443         * @param {Node} node The child node inserted
2444         * @param {Node} refNode The child node the node was inserted before
2445         */
2446        "insert" : true,
2447        /**
2448         * @event beforeappend
2449         * Fires before a new child is appended, return false to cancel the append.
2450         * @param {Tree} tree The owner tree
2451         * @param {Node} this This node
2452         * @param {Node} node The child node to be appended
2453         */
2454        "beforeappend" : true,
2455        /**
2456         * @event beforeremove
2457         * Fires before a child is removed, return false to cancel the remove.
2458         * @param {Tree} tree The owner tree
2459         * @param {Node} this This node
2460         * @param {Node} node The child node to be removed
2461         */
2462        "beforeremove" : true,
2463        /**
2464         * @event beforemove
2465         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
2466         * @param {Tree} tree The owner tree
2467         * @param {Node} this This node
2468         * @param {Node} oldParent The parent of this node
2469         * @param {Node} newParent The new parent this node is moving to
2470         * @param {Number} index The index it is being moved to
2471         */
2472        "beforemove" : true,
2473        /**
2474         * @event beforeinsert
2475         * Fires before a new child is inserted, return false to cancel the insert.
2476         * @param {Tree} tree The owner tree
2477         * @param {Node} this This node
2478         * @param {Node} node The child node to be inserted
2479         * @param {Node} refNode The child node the node is being inserted before
2480         */
2481        "beforeinsert" : true
2482    });
2483     this.listeners = this.attributes.listeners;
2484     Roo.data.Node.superclass.constructor.call(this);
2485 };
2486
2487 Roo.extend(Roo.data.Node, Roo.util.Observable, {
2488     fireEvent : function(evtName){
2489         // first do standard event for this node
2490         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
2491             return false;
2492         }
2493         // then bubble it up to the tree if the event wasn't cancelled
2494         var ot = this.getOwnerTree();
2495         if(ot){
2496             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
2497                 return false;
2498             }
2499         }
2500         return true;
2501     },
2502
2503     /**
2504      * Returns true if this node is a leaf
2505      * @return {Boolean}
2506      */
2507     isLeaf : function(){
2508         return this.leaf === true;
2509     },
2510
2511     // private
2512     setFirstChild : function(node){
2513         this.firstChild = node;
2514     },
2515
2516     //private
2517     setLastChild : function(node){
2518         this.lastChild = node;
2519     },
2520
2521
2522     /**
2523      * Returns true if this node is the last child of its parent
2524      * @return {Boolean}
2525      */
2526     isLast : function(){
2527        return (!this.parentNode ? true : this.parentNode.lastChild == this);
2528     },
2529
2530     /**
2531      * Returns true if this node is the first child of its parent
2532      * @return {Boolean}
2533      */
2534     isFirst : function(){
2535        return (!this.parentNode ? true : this.parentNode.firstChild == this);
2536     },
2537
2538     hasChildNodes : function(){
2539         return !this.isLeaf() && this.childNodes.length > 0;
2540     },
2541
2542     /**
2543      * Insert node(s) as the last child node of this node.
2544      * @param {Node/Array} node The node or Array of nodes to append
2545      * @return {Node} The appended node if single append, or null if an array was passed
2546      */
2547     appendChild : function(node){
2548         var multi = false;
2549         if(node instanceof Array){
2550             multi = node;
2551         }else if(arguments.length > 1){
2552             multi = arguments;
2553         }
2554         // if passed an array or multiple args do them one by one
2555         if(multi){
2556             for(var i = 0, len = multi.length; i < len; i++) {
2557                 this.appendChild(multi[i]);
2558             }
2559         }else{
2560             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
2561                 return false;
2562             }
2563             var index = this.childNodes.length;
2564             var oldParent = node.parentNode;
2565             // it's a move, make sure we move it cleanly
2566             if(oldParent){
2567                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
2568                     return false;
2569                 }
2570                 oldParent.removeChild(node);
2571             }
2572             index = this.childNodes.length;
2573             if(index == 0){
2574                 this.setFirstChild(node);
2575             }
2576             this.childNodes.push(node);
2577             node.parentNode = this;
2578             var ps = this.childNodes[index-1];
2579             if(ps){
2580                 node.previousSibling = ps;
2581                 ps.nextSibling = node;
2582             }else{
2583                 node.previousSibling = null;
2584             }
2585             node.nextSibling = null;
2586             this.setLastChild(node);
2587             node.setOwnerTree(this.getOwnerTree());
2588             this.fireEvent("append", this.ownerTree, this, node, index);
2589             if(oldParent){
2590                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
2591             }
2592             return node;
2593         }
2594     },
2595
2596     /**
2597      * Removes a child node from this node.
2598      * @param {Node} node The node to remove
2599      * @return {Node} The removed node
2600      */
2601     removeChild : function(node){
2602         var index = this.childNodes.indexOf(node);
2603         if(index == -1){
2604             return false;
2605         }
2606         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
2607             return false;
2608         }
2609
2610         // remove it from childNodes collection
2611         this.childNodes.splice(index, 1);
2612
2613         // update siblings
2614         if(node.previousSibling){
2615             node.previousSibling.nextSibling = node.nextSibling;
2616         }
2617         if(node.nextSibling){
2618             node.nextSibling.previousSibling = node.previousSibling;
2619         }
2620
2621         // update child refs
2622         if(this.firstChild == node){
2623             this.setFirstChild(node.nextSibling);
2624         }
2625         if(this.lastChild == node){
2626             this.setLastChild(node.previousSibling);
2627         }
2628
2629         node.setOwnerTree(null);
2630         // clear any references from the node
2631         node.parentNode = null;
2632         node.previousSibling = null;
2633         node.nextSibling = null;
2634         this.fireEvent("remove", this.ownerTree, this, node);
2635         return node;
2636     },
2637
2638     /**
2639      * Inserts the first node before the second node in this nodes childNodes collection.
2640      * @param {Node} node The node to insert
2641      * @param {Node} refNode The node to insert before (if null the node is appended)
2642      * @return {Node} The inserted node
2643      */
2644     insertBefore : function(node, refNode){
2645         if(!refNode){ // like standard Dom, refNode can be null for append
2646             return this.appendChild(node);
2647         }
2648         // nothing to do
2649         if(node == refNode){
2650             return false;
2651         }
2652
2653         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
2654             return false;
2655         }
2656         var index = this.childNodes.indexOf(refNode);
2657         var oldParent = node.parentNode;
2658         var refIndex = index;
2659
2660         // when moving internally, indexes will change after remove
2661         if(oldParent == this && this.childNodes.indexOf(node) < index){
2662             refIndex--;
2663         }
2664
2665         // it's a move, make sure we move it cleanly
2666         if(oldParent){
2667             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
2668                 return false;
2669             }
2670             oldParent.removeChild(node);
2671         }
2672         if(refIndex == 0){
2673             this.setFirstChild(node);
2674         }
2675         this.childNodes.splice(refIndex, 0, node);
2676         node.parentNode = this;
2677         var ps = this.childNodes[refIndex-1];
2678         if(ps){
2679             node.previousSibling = ps;
2680             ps.nextSibling = node;
2681         }else{
2682             node.previousSibling = null;
2683         }
2684         node.nextSibling = refNode;
2685         refNode.previousSibling = node;
2686         node.setOwnerTree(this.getOwnerTree());
2687         this.fireEvent("insert", this.ownerTree, this, node, refNode);
2688         if(oldParent){
2689             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
2690         }
2691         return node;
2692     },
2693
2694     /**
2695      * Returns the child node at the specified index.
2696      * @param {Number} index
2697      * @return {Node}
2698      */
2699     item : function(index){
2700         return this.childNodes[index];
2701     },
2702
2703     /**
2704      * Replaces one child node in this node with another.
2705      * @param {Node} newChild The replacement node
2706      * @param {Node} oldChild The node to replace
2707      * @return {Node} The replaced node
2708      */
2709     replaceChild : function(newChild, oldChild){
2710         this.insertBefore(newChild, oldChild);
2711         this.removeChild(oldChild);
2712         return oldChild;
2713     },
2714
2715     /**
2716      * Returns the index of a child node
2717      * @param {Node} node
2718      * @return {Number} The index of the node or -1 if it was not found
2719      */
2720     indexOf : function(child){
2721         return this.childNodes.indexOf(child);
2722     },
2723
2724     /**
2725      * Returns the tree this node is in.
2726      * @return {Tree}
2727      */
2728     getOwnerTree : function(){
2729         // if it doesn't have one, look for one
2730         if(!this.ownerTree){
2731             var p = this;
2732             while(p){
2733                 if(p.ownerTree){
2734                     this.ownerTree = p.ownerTree;
2735                     break;
2736                 }
2737                 p = p.parentNode;
2738             }
2739         }
2740         return this.ownerTree;
2741     },
2742
2743     /**
2744      * Returns depth of this node (the root node has a depth of 0)
2745      * @return {Number}
2746      */
2747     getDepth : function(){
2748         var depth = 0;
2749         var p = this;
2750         while(p.parentNode){
2751             ++depth;
2752             p = p.parentNode;
2753         }
2754         return depth;
2755     },
2756
2757     // private
2758     setOwnerTree : function(tree){
2759         // if it's move, we need to update everyone
2760         if(tree != this.ownerTree){
2761             if(this.ownerTree){
2762                 this.ownerTree.unregisterNode(this);
2763             }
2764             this.ownerTree = tree;
2765             var cs = this.childNodes;
2766             for(var i = 0, len = cs.length; i < len; i++) {
2767                 cs[i].setOwnerTree(tree);
2768             }
2769             if(tree){
2770                 tree.registerNode(this);
2771             }
2772         }
2773     },
2774
2775     /**
2776      * Returns the path for this node. The path can be used to expand or select this node programmatically.
2777      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
2778      * @return {String} The path
2779      */
2780     getPath : function(attr){
2781         attr = attr || "id";
2782         var p = this.parentNode;
2783         var b = [this.attributes[attr]];
2784         while(p){
2785             b.unshift(p.attributes[attr]);
2786             p = p.parentNode;
2787         }
2788         var sep = this.getOwnerTree().pathSeparator;
2789         return sep + b.join(sep);
2790     },
2791
2792     /**
2793      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2794      * function call will be the scope provided or the current node. The arguments to the function
2795      * will be the args provided or the current node. If the function returns false at any point,
2796      * the bubble is stopped.
2797      * @param {Function} fn The function to call
2798      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2799      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2800      */
2801     bubble : function(fn, scope, args){
2802         var p = this;
2803         while(p){
2804             if(fn.call(scope || p, args || p) === false){
2805                 break;
2806             }
2807             p = p.parentNode;
2808         }
2809     },
2810
2811     /**
2812      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2813      * function call will be the scope provided or the current node. The arguments to the function
2814      * will be the args provided or the current node. If the function returns false at any point,
2815      * the cascade is stopped on that branch.
2816      * @param {Function} fn The function to call
2817      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2818      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2819      */
2820     cascade : function(fn, scope, args){
2821         if(fn.call(scope || this, args || this) !== false){
2822             var cs = this.childNodes;
2823             for(var i = 0, len = cs.length; i < len; i++) {
2824                 cs[i].cascade(fn, scope, args);
2825             }
2826         }
2827     },
2828
2829     /**
2830      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
2831      * function call will be the scope provided or the current node. The arguments to the function
2832      * will be the args provided or the current node. If the function returns false at any point,
2833      * the iteration stops.
2834      * @param {Function} fn The function to call
2835      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2836      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2837      */
2838     eachChild : function(fn, scope, args){
2839         var cs = this.childNodes;
2840         for(var i = 0, len = cs.length; i < len; i++) {
2841                 if(fn.call(scope || this, args || cs[i]) === false){
2842                     break;
2843                 }
2844         }
2845     },
2846
2847     /**
2848      * Finds the first child that has the attribute with the specified value.
2849      * @param {String} attribute The attribute name
2850      * @param {Mixed} value The value to search for
2851      * @return {Node} The found child or null if none was found
2852      */
2853     findChild : function(attribute, value){
2854         var cs = this.childNodes;
2855         for(var i = 0, len = cs.length; i < len; i++) {
2856                 if(cs[i].attributes[attribute] == value){
2857                     return cs[i];
2858                 }
2859         }
2860         return null;
2861     },
2862
2863     /**
2864      * Finds the first child by a custom function. The child matches if the function passed
2865      * returns true.
2866      * @param {Function} fn
2867      * @param {Object} scope (optional)
2868      * @return {Node} The found child or null if none was found
2869      */
2870     findChildBy : function(fn, scope){
2871         var cs = this.childNodes;
2872         for(var i = 0, len = cs.length; i < len; i++) {
2873                 if(fn.call(scope||cs[i], cs[i]) === true){
2874                     return cs[i];
2875                 }
2876         }
2877         return null;
2878     },
2879
2880     /**
2881      * Sorts this nodes children using the supplied sort function
2882      * @param {Function} fn
2883      * @param {Object} scope (optional)
2884      */
2885     sort : function(fn, scope){
2886         var cs = this.childNodes;
2887         var len = cs.length;
2888         if(len > 0){
2889             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
2890             cs.sort(sortFn);
2891             for(var i = 0; i < len; i++){
2892                 var n = cs[i];
2893                 n.previousSibling = cs[i-1];
2894                 n.nextSibling = cs[i+1];
2895                 if(i == 0){
2896                     this.setFirstChild(n);
2897                 }
2898                 if(i == len-1){
2899                     this.setLastChild(n);
2900                 }
2901             }
2902         }
2903     },
2904
2905     /**
2906      * Returns true if this node is an ancestor (at any point) of the passed node.
2907      * @param {Node} node
2908      * @return {Boolean}
2909      */
2910     contains : function(node){
2911         return node.isAncestor(this);
2912     },
2913
2914     /**
2915      * Returns true if the passed node is an ancestor (at any point) of this node.
2916      * @param {Node} node
2917      * @return {Boolean}
2918      */
2919     isAncestor : function(node){
2920         var p = this.parentNode;
2921         while(p){
2922             if(p == node){
2923                 return true;
2924             }
2925             p = p.parentNode;
2926         }
2927         return false;
2928     },
2929
2930     toString : function(){
2931         return "[Node"+(this.id?" "+this.id:"")+"]";
2932     }
2933 });/*
2934  * Based on:
2935  * Ext JS Library 1.1.1
2936  * Copyright(c) 2006-2007, Ext JS, LLC.
2937  *
2938  * Originally Released Under LGPL - original licence link has changed is not relivant.
2939  *
2940  * Fork - LGPL
2941  * <script type="text/javascript">
2942  */
2943  (function(){ 
2944 /**
2945  * @class Roo.Layer
2946  * @extends Roo.Element
2947  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
2948  * automatic maintaining of shadow/shim positions.
2949  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
2950  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
2951  * you can pass a string with a CSS class name. False turns off the shadow.
2952  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
2953  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
2954  * @cfg {String} cls CSS class to add to the element
2955  * @cfg {Number} zindex Starting z-index (defaults to 11000)
2956  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
2957  * @constructor
2958  * @param {Object} config An object with config options.
2959  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
2960  */
2961
2962 Roo.Layer = function(config, existingEl){
2963     config = config || {};
2964     var dh = Roo.DomHelper;
2965     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
2966     if(existingEl){
2967         this.dom = Roo.getDom(existingEl);
2968     }
2969     if(!this.dom){
2970         var o = config.dh || {tag: "div", cls: "x-layer"};
2971         this.dom = dh.append(pel, o);
2972     }
2973     if(config.cls){
2974         this.addClass(config.cls);
2975     }
2976     this.constrain = config.constrain !== false;
2977     this.visibilityMode = Roo.Element.VISIBILITY;
2978     if(config.id){
2979         this.id = this.dom.id = config.id;
2980     }else{
2981         this.id = Roo.id(this.dom);
2982     }
2983     this.zindex = config.zindex || this.getZIndex();
2984     this.position("absolute", this.zindex);
2985     if(config.shadow){
2986         this.shadowOffset = config.shadowOffset || 4;
2987         this.shadow = new Roo.Shadow({
2988             offset : this.shadowOffset,
2989             mode : config.shadow
2990         });
2991     }else{
2992         this.shadowOffset = 0;
2993     }
2994     this.useShim = config.shim !== false && Roo.useShims;
2995     this.useDisplay = config.useDisplay;
2996     this.hide();
2997 };
2998
2999 var supr = Roo.Element.prototype;
3000
3001 // shims are shared among layer to keep from having 100 iframes
3002 var shims = [];
3003
3004 Roo.extend(Roo.Layer, Roo.Element, {
3005
3006     getZIndex : function(){
3007         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
3008     },
3009
3010     getShim : function(){
3011         if(!this.useShim){
3012             return null;
3013         }
3014         if(this.shim){
3015             return this.shim;
3016         }
3017         var shim = shims.shift();
3018         if(!shim){
3019             shim = this.createShim();
3020             shim.enableDisplayMode('block');
3021             shim.dom.style.display = 'none';
3022             shim.dom.style.visibility = 'visible';
3023         }
3024         var pn = this.dom.parentNode;
3025         if(shim.dom.parentNode != pn){
3026             pn.insertBefore(shim.dom, this.dom);
3027         }
3028         shim.setStyle('z-index', this.getZIndex()-2);
3029         this.shim = shim;
3030         return shim;
3031     },
3032
3033     hideShim : function(){
3034         if(this.shim){
3035             this.shim.setDisplayed(false);
3036             shims.push(this.shim);
3037             delete this.shim;
3038         }
3039     },
3040
3041     disableShadow : function(){
3042         if(this.shadow){
3043             this.shadowDisabled = true;
3044             this.shadow.hide();
3045             this.lastShadowOffset = this.shadowOffset;
3046             this.shadowOffset = 0;
3047         }
3048     },
3049
3050     enableShadow : function(show){
3051         if(this.shadow){
3052             this.shadowDisabled = false;
3053             this.shadowOffset = this.lastShadowOffset;
3054             delete this.lastShadowOffset;
3055             if(show){
3056                 this.sync(true);
3057             }
3058         }
3059     },
3060
3061     // private
3062     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
3063     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
3064     sync : function(doShow){
3065         var sw = this.shadow;
3066         if(!this.updating && this.isVisible() && (sw || this.useShim)){
3067             var sh = this.getShim();
3068
3069             var w = this.getWidth(),
3070                 h = this.getHeight();
3071
3072             var l = this.getLeft(true),
3073                 t = this.getTop(true);
3074
3075             if(sw && !this.shadowDisabled){
3076                 if(doShow && !sw.isVisible()){
3077                     sw.show(this);
3078                 }else{
3079                     sw.realign(l, t, w, h);
3080                 }
3081                 if(sh){
3082                     if(doShow){
3083                        sh.show();
3084                     }
3085                     // fit the shim behind the shadow, so it is shimmed too
3086                     var a = sw.adjusts, s = sh.dom.style;
3087                     s.left = (Math.min(l, l+a.l))+"px";
3088                     s.top = (Math.min(t, t+a.t))+"px";
3089                     s.width = (w+a.w)+"px";
3090                     s.height = (h+a.h)+"px";
3091                 }
3092             }else if(sh){
3093                 if(doShow){
3094                    sh.show();
3095                 }
3096                 sh.setSize(w, h);
3097                 sh.setLeftTop(l, t);
3098             }
3099             
3100         }
3101     },
3102
3103     // private
3104     destroy : function(){
3105         this.hideShim();
3106         if(this.shadow){
3107             this.shadow.hide();
3108         }
3109         this.removeAllListeners();
3110         var pn = this.dom.parentNode;
3111         if(pn){
3112             pn.removeChild(this.dom);
3113         }
3114         Roo.Element.uncache(this.id);
3115     },
3116
3117     remove : function(){
3118         this.destroy();
3119     },
3120
3121     // private
3122     beginUpdate : function(){
3123         this.updating = true;
3124     },
3125
3126     // private
3127     endUpdate : function(){
3128         this.updating = false;
3129         this.sync(true);
3130     },
3131
3132     // private
3133     hideUnders : function(negOffset){
3134         if(this.shadow){
3135             this.shadow.hide();
3136         }
3137         this.hideShim();
3138     },
3139
3140     // private
3141     constrainXY : function(){
3142         if(this.constrain){
3143             var vw = Roo.lib.Dom.getViewWidth(),
3144                 vh = Roo.lib.Dom.getViewHeight();
3145             var s = Roo.get(document).getScroll();
3146
3147             var xy = this.getXY();
3148             var x = xy[0], y = xy[1];   
3149             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
3150             // only move it if it needs it
3151             var moved = false;
3152             // first validate right/bottom
3153             if((x + w) > vw+s.left){
3154                 x = vw - w - this.shadowOffset;
3155                 moved = true;
3156             }
3157             if((y + h) > vh+s.top){
3158                 y = vh - h - this.shadowOffset;
3159                 moved = true;
3160             }
3161             // then make sure top/left isn't negative
3162             if(x < s.left){
3163                 x = s.left;
3164                 moved = true;
3165             }
3166             if(y < s.top){
3167                 y = s.top;
3168                 moved = true;
3169             }
3170             if(moved){
3171                 if(this.avoidY){
3172                     var ay = this.avoidY;
3173                     if(y <= ay && (y+h) >= ay){
3174                         y = ay-h-5;   
3175                     }
3176                 }
3177                 xy = [x, y];
3178                 this.storeXY(xy);
3179                 supr.setXY.call(this, xy);
3180                 this.sync();
3181             }
3182         }
3183     },
3184
3185     isVisible : function(){
3186         return this.visible;    
3187     },
3188
3189     // private
3190     showAction : function(){
3191         this.visible = true; // track visibility to prevent getStyle calls
3192         if(this.useDisplay === true){
3193             this.setDisplayed("");
3194         }else if(this.lastXY){
3195             supr.setXY.call(this, this.lastXY);
3196         }else if(this.lastLT){
3197             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
3198         }
3199     },
3200
3201     // private
3202     hideAction : function(){
3203         this.visible = false;
3204         if(this.useDisplay === true){
3205             this.setDisplayed(false);
3206         }else{
3207             this.setLeftTop(-10000,-10000);
3208         }
3209     },
3210
3211     // overridden Element method
3212     setVisible : function(v, a, d, c, e){
3213         if(v){
3214             this.showAction();
3215         }
3216         if(a && v){
3217             var cb = function(){
3218                 this.sync(true);
3219                 if(c){
3220                     c();
3221                 }
3222             }.createDelegate(this);
3223             supr.setVisible.call(this, true, true, d, cb, e);
3224         }else{
3225             if(!v){
3226                 this.hideUnders(true);
3227             }
3228             var cb = c;
3229             if(a){
3230                 cb = function(){
3231                     this.hideAction();
3232                     if(c){
3233                         c();
3234                     }
3235                 }.createDelegate(this);
3236             }
3237             supr.setVisible.call(this, v, a, d, cb, e);
3238             if(v){
3239                 this.sync(true);
3240             }else if(!a){
3241                 this.hideAction();
3242             }
3243         }
3244     },
3245
3246     storeXY : function(xy){
3247         delete this.lastLT;
3248         this.lastXY = xy;
3249     },
3250
3251     storeLeftTop : function(left, top){
3252         delete this.lastXY;
3253         this.lastLT = [left, top];
3254     },
3255
3256     // private
3257     beforeFx : function(){
3258         this.beforeAction();
3259         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
3260     },
3261
3262     // private
3263     afterFx : function(){
3264         Roo.Layer.superclass.afterFx.apply(this, arguments);
3265         this.sync(this.isVisible());
3266     },
3267
3268     // private
3269     beforeAction : function(){
3270         if(!this.updating && this.shadow){
3271             this.shadow.hide();
3272         }
3273     },
3274
3275     // overridden Element method
3276     setLeft : function(left){
3277         this.storeLeftTop(left, this.getTop(true));
3278         supr.setLeft.apply(this, arguments);
3279         this.sync();
3280     },
3281
3282     setTop : function(top){
3283         this.storeLeftTop(this.getLeft(true), top);
3284         supr.setTop.apply(this, arguments);
3285         this.sync();
3286     },
3287
3288     setLeftTop : function(left, top){
3289         this.storeLeftTop(left, top);
3290         supr.setLeftTop.apply(this, arguments);
3291         this.sync();
3292     },
3293
3294     setXY : function(xy, a, d, c, e){
3295         this.fixDisplay();
3296         this.beforeAction();
3297         this.storeXY(xy);
3298         var cb = this.createCB(c);
3299         supr.setXY.call(this, xy, a, d, cb, e);
3300         if(!a){
3301             cb();
3302         }
3303     },
3304
3305     // private
3306     createCB : function(c){
3307         var el = this;
3308         return function(){
3309             el.constrainXY();
3310             el.sync(true);
3311             if(c){
3312                 c();
3313             }
3314         };
3315     },
3316
3317     // overridden Element method
3318     setX : function(x, a, d, c, e){
3319         this.setXY([x, this.getY()], a, d, c, e);
3320     },
3321
3322     // overridden Element method
3323     setY : function(y, a, d, c, e){
3324         this.setXY([this.getX(), y], a, d, c, e);
3325     },
3326
3327     // overridden Element method
3328     setSize : function(w, h, a, d, c, e){
3329         this.beforeAction();
3330         var cb = this.createCB(c);
3331         supr.setSize.call(this, w, h, a, d, cb, e);
3332         if(!a){
3333             cb();
3334         }
3335     },
3336
3337     // overridden Element method
3338     setWidth : function(w, a, d, c, e){
3339         this.beforeAction();
3340         var cb = this.createCB(c);
3341         supr.setWidth.call(this, w, a, d, cb, e);
3342         if(!a){
3343             cb();
3344         }
3345     },
3346
3347     // overridden Element method
3348     setHeight : function(h, a, d, c, e){
3349         this.beforeAction();
3350         var cb = this.createCB(c);
3351         supr.setHeight.call(this, h, a, d, cb, e);
3352         if(!a){
3353             cb();
3354         }
3355     },
3356
3357     // overridden Element method
3358     setBounds : function(x, y, w, h, a, d, c, e){
3359         this.beforeAction();
3360         var cb = this.createCB(c);
3361         if(!a){
3362             this.storeXY([x, y]);
3363             supr.setXY.call(this, [x, y]);
3364             supr.setSize.call(this, w, h, a, d, cb, e);
3365             cb();
3366         }else{
3367             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
3368         }
3369         return this;
3370     },
3371     
3372     /**
3373      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
3374      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
3375      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
3376      * @param {Number} zindex The new z-index to set
3377      * @return {this} The Layer
3378      */
3379     setZIndex : function(zindex){
3380         this.zindex = zindex;
3381         this.setStyle("z-index", zindex + 2);
3382         if(this.shadow){
3383             this.shadow.setZIndex(zindex + 1);
3384         }
3385         if(this.shim){
3386             this.shim.setStyle("z-index", zindex);
3387         }
3388     }
3389 });
3390 })();/*
3391  * Based on:
3392  * Ext JS Library 1.1.1
3393  * Copyright(c) 2006-2007, Ext JS, LLC.
3394  *
3395  * Originally Released Under LGPL - original licence link has changed is not relivant.
3396  *
3397  * Fork - LGPL
3398  * <script type="text/javascript">
3399  */
3400
3401
3402 /**
3403  * @class Roo.Shadow
3404  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
3405  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
3406  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
3407  * @constructor
3408  * Create a new Shadow
3409  * @param {Object} config The config object
3410  */
3411 Roo.Shadow = function(config){
3412     Roo.apply(this, config);
3413     if(typeof this.mode != "string"){
3414         this.mode = this.defaultMode;
3415     }
3416     var o = this.offset, a = {h: 0};
3417     var rad = Math.floor(this.offset/2);
3418     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
3419         case "drop":
3420             a.w = 0;
3421             a.l = a.t = o;
3422             a.t -= 1;
3423             if(Roo.isIE){
3424                 a.l -= this.offset + rad;
3425                 a.t -= this.offset + rad;
3426                 a.w -= rad;
3427                 a.h -= rad;
3428                 a.t += 1;
3429             }
3430         break;
3431         case "sides":
3432             a.w = (o*2);
3433             a.l = -o;
3434             a.t = o-1;
3435             if(Roo.isIE){
3436                 a.l -= (this.offset - rad);
3437                 a.t -= this.offset + rad;
3438                 a.l += 1;
3439                 a.w -= (this.offset - rad)*2;
3440                 a.w -= rad + 1;
3441                 a.h -= 1;
3442             }
3443         break;
3444         case "frame":
3445             a.w = a.h = (o*2);
3446             a.l = a.t = -o;
3447             a.t += 1;
3448             a.h -= 2;
3449             if(Roo.isIE){
3450                 a.l -= (this.offset - rad);
3451                 a.t -= (this.offset - rad);
3452                 a.l += 1;
3453                 a.w -= (this.offset + rad + 1);
3454                 a.h -= (this.offset + rad);
3455                 a.h += 1;
3456             }
3457         break;
3458     };
3459
3460     this.adjusts = a;
3461 };
3462
3463 Roo.Shadow.prototype = {
3464     /**
3465      * @cfg {String} mode
3466      * The shadow display mode.  Supports the following options:<br />
3467      * sides: Shadow displays on both sides and bottom only<br />
3468      * frame: Shadow displays equally on all four sides<br />
3469      * drop: Traditional bottom-right drop shadow (default)
3470      */
3471     /**
3472      * @cfg {String} offset
3473      * The number of pixels to offset the shadow from the element (defaults to 4)
3474      */
3475     offset: 4,
3476
3477     // private
3478     defaultMode: "drop",
3479
3480     /**
3481      * Displays the shadow under the target element
3482      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
3483      */
3484     show : function(target){
3485         target = Roo.get(target);
3486         if(!this.el){
3487             this.el = Roo.Shadow.Pool.pull();
3488             if(this.el.dom.nextSibling != target.dom){
3489                 this.el.insertBefore(target);
3490             }
3491         }
3492         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
3493         if(Roo.isIE){
3494             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
3495         }
3496         this.realign(
3497             target.getLeft(true),
3498             target.getTop(true),
3499             target.getWidth(),
3500             target.getHeight()
3501         );
3502         this.el.dom.style.display = "block";
3503     },
3504
3505     /**
3506      * Returns true if the shadow is visible, else false
3507      */
3508     isVisible : function(){
3509         return this.el ? true : false;  
3510     },
3511
3512     /**
3513      * Direct alignment when values are already available. Show must be called at least once before
3514      * calling this method to ensure it is initialized.
3515      * @param {Number} left The target element left position
3516      * @param {Number} top The target element top position
3517      * @param {Number} width The target element width
3518      * @param {Number} height The target element height
3519      */
3520     realign : function(l, t, w, h){
3521         if(!this.el){
3522             return;
3523         }
3524         var a = this.adjusts, d = this.el.dom, s = d.style;
3525         var iea = 0;
3526         s.left = (l+a.l)+"px";
3527         s.top = (t+a.t)+"px";
3528         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
3529  
3530         if(s.width != sws || s.height != shs){
3531             s.width = sws;
3532             s.height = shs;
3533             if(!Roo.isIE){
3534                 var cn = d.childNodes;
3535                 var sww = Math.max(0, (sw-12))+"px";
3536                 cn[0].childNodes[1].style.width = sww;
3537                 cn[1].childNodes[1].style.width = sww;
3538                 cn[2].childNodes[1].style.width = sww;
3539                 cn[1].style.height = Math.max(0, (sh-12))+"px";
3540             }
3541         }
3542     },
3543
3544     /**
3545      * Hides this shadow
3546      */
3547     hide : function(){
3548         if(this.el){
3549             this.el.dom.style.display = "none";
3550             Roo.Shadow.Pool.push(this.el);
3551             delete this.el;
3552         }
3553     },
3554
3555     /**
3556      * Adjust the z-index of this shadow
3557      * @param {Number} zindex The new z-index
3558      */
3559     setZIndex : function(z){
3560         this.zIndex = z;
3561         if(this.el){
3562             this.el.setStyle("z-index", z);
3563         }
3564     }
3565 };
3566
3567 // Private utility class that manages the internal Shadow cache
3568 Roo.Shadow.Pool = function(){
3569     var p = [];
3570     var markup = Roo.isIE ?
3571                  '<div class="x-ie-shadow"></div>' :
3572                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
3573     return {
3574         pull : function(){
3575             var sh = p.shift();
3576             if(!sh){
3577                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
3578                 sh.autoBoxAdjust = false;
3579             }
3580             return sh;
3581         },
3582
3583         push : function(sh){
3584             p.push(sh);
3585         }
3586     };
3587 }();/*
3588  * Based on:
3589  * Ext JS Library 1.1.1
3590  * Copyright(c) 2006-2007, Ext JS, LLC.
3591  *
3592  * Originally Released Under LGPL - original licence link has changed is not relivant.
3593  *
3594  * Fork - LGPL
3595  * <script type="text/javascript">
3596  */
3597
3598
3599 /**
3600  * @class Roo.SplitBar
3601  * @extends Roo.util.Observable
3602  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
3603  * <br><br>
3604  * Usage:
3605  * <pre><code>
3606 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
3607                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
3608 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
3609 split.minSize = 100;
3610 split.maxSize = 600;
3611 split.animate = true;
3612 split.on('moved', splitterMoved);
3613 </code></pre>
3614  * @constructor
3615  * Create a new SplitBar
3616  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
3617  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
3618  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3619  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
3620                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
3621                         position of the SplitBar).
3622  */
3623 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
3624     
3625     /** @private */
3626     this.el = Roo.get(dragElement, true);
3627     this.el.dom.unselectable = "on";
3628     /** @private */
3629     this.resizingEl = Roo.get(resizingElement, true);
3630
3631     /**
3632      * @private
3633      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3634      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
3635      * @type Number
3636      */
3637     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
3638     
3639     /**
3640      * The minimum size of the resizing element. (Defaults to 0)
3641      * @type Number
3642      */
3643     this.minSize = 0;
3644     
3645     /**
3646      * The maximum size of the resizing element. (Defaults to 2000)
3647      * @type Number
3648      */
3649     this.maxSize = 2000;
3650     
3651     /**
3652      * Whether to animate the transition to the new size
3653      * @type Boolean
3654      */
3655     this.animate = false;
3656     
3657     /**
3658      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
3659      * @type Boolean
3660      */
3661     this.useShim = false;
3662     
3663     /** @private */
3664     this.shim = null;
3665     
3666     if(!existingProxy){
3667         /** @private */
3668         this.proxy = Roo.SplitBar.createProxy(this.orientation);
3669     }else{
3670         this.proxy = Roo.get(existingProxy).dom;
3671     }
3672     /** @private */
3673     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
3674     
3675     /** @private */
3676     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
3677     
3678     /** @private */
3679     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
3680     
3681     /** @private */
3682     this.dragSpecs = {};
3683     
3684     /**
3685      * @private The adapter to use to positon and resize elements
3686      */
3687     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
3688     this.adapter.init(this);
3689     
3690     if(this.orientation == Roo.SplitBar.HORIZONTAL){
3691         /** @private */
3692         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
3693         this.el.addClass("x-splitbar-h");
3694     }else{
3695         /** @private */
3696         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
3697         this.el.addClass("x-splitbar-v");
3698     }
3699     
3700     this.addEvents({
3701         /**
3702          * @event resize
3703          * Fires when the splitter is moved (alias for {@link #event-moved})
3704          * @param {Roo.SplitBar} this
3705          * @param {Number} newSize the new width or height
3706          */
3707         "resize" : true,
3708         /**
3709          * @event moved
3710          * Fires when the splitter is moved
3711          * @param {Roo.SplitBar} this
3712          * @param {Number} newSize the new width or height
3713          */
3714         "moved" : true,
3715         /**
3716          * @event beforeresize
3717          * Fires before the splitter is dragged
3718          * @param {Roo.SplitBar} this
3719          */
3720         "beforeresize" : true,
3721
3722         "beforeapply" : true
3723     });
3724
3725     Roo.util.Observable.call(this);
3726 };
3727
3728 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
3729     onStartProxyDrag : function(x, y){
3730         this.fireEvent("beforeresize", this);
3731         if(!this.overlay){
3732             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
3733             o.unselectable();
3734             o.enableDisplayMode("block");
3735             // all splitbars share the same overlay
3736             Roo.SplitBar.prototype.overlay = o;
3737         }
3738         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
3739         this.overlay.show();
3740         Roo.get(this.proxy).setDisplayed("block");
3741         var size = this.adapter.getElementSize(this);
3742         this.activeMinSize = this.getMinimumSize();;
3743         this.activeMaxSize = this.getMaximumSize();;
3744         var c1 = size - this.activeMinSize;
3745         var c2 = Math.max(this.activeMaxSize - size, 0);
3746         if(this.orientation == Roo.SplitBar.HORIZONTAL){
3747             this.dd.resetConstraints();
3748             this.dd.setXConstraint(
3749                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
3750                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
3751             );
3752             this.dd.setYConstraint(0, 0);
3753         }else{
3754             this.dd.resetConstraints();
3755             this.dd.setXConstraint(0, 0);
3756             this.dd.setYConstraint(
3757                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
3758                 this.placement == Roo.SplitBar.TOP ? c2 : c1
3759             );
3760          }
3761         this.dragSpecs.startSize = size;
3762         this.dragSpecs.startPoint = [x, y];
3763         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
3764     },
3765     
3766     /** 
3767      * @private Called after the drag operation by the DDProxy
3768      */
3769     onEndProxyDrag : function(e){
3770         Roo.get(this.proxy).setDisplayed(false);
3771         var endPoint = Roo.lib.Event.getXY(e);
3772         if(this.overlay){
3773             this.overlay.hide();
3774         }
3775         var newSize;
3776         if(this.orientation == Roo.SplitBar.HORIZONTAL){
3777             newSize = this.dragSpecs.startSize + 
3778                 (this.placement == Roo.SplitBar.LEFT ?
3779                     endPoint[0] - this.dragSpecs.startPoint[0] :
3780                     this.dragSpecs.startPoint[0] - endPoint[0]
3781                 );
3782         }else{
3783             newSize = this.dragSpecs.startSize + 
3784                 (this.placement == Roo.SplitBar.TOP ?
3785                     endPoint[1] - this.dragSpecs.startPoint[1] :
3786                     this.dragSpecs.startPoint[1] - endPoint[1]
3787                 );
3788         }
3789         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
3790         if(newSize != this.dragSpecs.startSize){
3791             if(this.fireEvent('beforeapply', this, newSize) !== false){
3792                 this.adapter.setElementSize(this, newSize);
3793                 this.fireEvent("moved", this, newSize);
3794                 this.fireEvent("resize", this, newSize);
3795             }
3796         }
3797     },
3798     
3799     /**
3800      * Get the adapter this SplitBar uses
3801      * @return The adapter object
3802      */
3803     getAdapter : function(){
3804         return this.adapter;
3805     },
3806     
3807     /**
3808      * Set the adapter this SplitBar uses
3809      * @param {Object} adapter A SplitBar adapter object
3810      */
3811     setAdapter : function(adapter){
3812         this.adapter = adapter;
3813         this.adapter.init(this);
3814     },
3815     
3816     /**
3817      * Gets the minimum size for the resizing element
3818      * @return {Number} The minimum size
3819      */
3820     getMinimumSize : function(){
3821         return this.minSize;
3822     },
3823     
3824     /**
3825      * Sets the minimum size for the resizing element
3826      * @param {Number} minSize The minimum size
3827      */
3828     setMinimumSize : function(minSize){
3829         this.minSize = minSize;
3830     },
3831     
3832     /**
3833      * Gets the maximum size for the resizing element
3834      * @return {Number} The maximum size
3835      */
3836     getMaximumSize : function(){
3837         return this.maxSize;
3838     },
3839     
3840     /**
3841      * Sets the maximum size for the resizing element
3842      * @param {Number} maxSize The maximum size
3843      */
3844     setMaximumSize : function(maxSize){
3845         this.maxSize = maxSize;
3846     },
3847     
3848     /**
3849      * Sets the initialize size for the resizing element
3850      * @param {Number} size The initial size
3851      */
3852     setCurrentSize : function(size){
3853         var oldAnimate = this.animate;
3854         this.animate = false;
3855         this.adapter.setElementSize(this, size);
3856         this.animate = oldAnimate;
3857     },
3858     
3859     /**
3860      * Destroy this splitbar. 
3861      * @param {Boolean} removeEl True to remove the element
3862      */
3863     destroy : function(removeEl){
3864         if(this.shim){
3865             this.shim.remove();
3866         }
3867         this.dd.unreg();
3868         this.proxy.parentNode.removeChild(this.proxy);
3869         if(removeEl){
3870             this.el.remove();
3871         }
3872     }
3873 });
3874
3875 /**
3876  * @private static Create our own proxy element element. So it will be the same same size on all browsers, we won't use borders. Instead we use a background color.
3877  */
3878 Roo.SplitBar.createProxy = function(dir){
3879     var proxy = new Roo.Element(document.createElement("div"));
3880     proxy.unselectable();
3881     var cls = 'x-splitbar-proxy';
3882     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
3883     document.body.appendChild(proxy.dom);
3884     return proxy.dom;
3885 };
3886
3887 /** 
3888  * @class Roo.SplitBar.BasicLayoutAdapter
3889  * Default Adapter. It assumes the splitter and resizing element are not positioned
3890  * elements and only gets/sets the width of the element. Generally used for table based layouts.
3891  */
3892 Roo.SplitBar.BasicLayoutAdapter = function(){
3893 };
3894
3895 Roo.SplitBar.BasicLayoutAdapter.prototype = {
3896     // do nothing for now
3897     init : function(s){
3898     
3899     },
3900     /**
3901      * Called before drag operations to get the current size of the resizing element. 
3902      * @param {Roo.SplitBar} s The SplitBar using this adapter
3903      */
3904      getElementSize : function(s){
3905         if(s.orientation == Roo.SplitBar.HORIZONTAL){
3906             return s.resizingEl.getWidth();
3907         }else{
3908             return s.resizingEl.getHeight();
3909         }
3910     },
3911     
3912     /**
3913      * Called after drag operations to set the size of the resizing element.
3914      * @param {Roo.SplitBar} s The SplitBar using this adapter
3915      * @param {Number} newSize The new size to set
3916      * @param {Function} onComplete A function to be invoked when resizing is complete
3917      */
3918     setElementSize : function(s, newSize, onComplete){
3919         if(s.orientation == Roo.SplitBar.HORIZONTAL){
3920             if(!s.animate){
3921                 s.resizingEl.setWidth(newSize);
3922                 if(onComplete){
3923                     onComplete(s, newSize);
3924                 }
3925             }else{
3926                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
3927             }
3928         }else{
3929             
3930             if(!s.animate){
3931                 s.resizingEl.setHeight(newSize);
3932                 if(onComplete){
3933                     onComplete(s, newSize);
3934                 }
3935             }else{
3936                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
3937             }
3938         }
3939     }
3940 };
3941
3942 /** 
3943  *@class Roo.SplitBar.AbsoluteLayoutAdapter
3944  * @extends Roo.SplitBar.BasicLayoutAdapter
3945  * Adapter that  moves the splitter element to align with the resized sizing element. 
3946  * Used with an absolute positioned SplitBar.
3947  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
3948  * document.body, make sure you assign an id to the body element.
3949  */
3950 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
3951     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
3952     this.container = Roo.get(container);
3953 };
3954
3955 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
3956     init : function(s){
3957         this.basic.init(s);
3958     },
3959     
3960     getElementSize : function(s){
3961         return this.basic.getElementSize(s);
3962     },
3963     
3964     setElementSize : function(s, newSize, onComplete){
3965         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
3966     },
3967     
3968     moveSplitter : function(s){
3969         var yes = Roo.SplitBar;
3970         switch(s.placement){
3971             case yes.LEFT:
3972                 s.el.setX(s.resizingEl.getRight());
3973                 break;
3974             case yes.RIGHT:
3975                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
3976                 break;
3977             case yes.TOP:
3978                 s.el.setY(s.resizingEl.getBottom());
3979                 break;
3980             case yes.BOTTOM:
3981                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
3982                 break;
3983         }
3984     }
3985 };
3986
3987 /**
3988  * Orientation constant - Create a vertical SplitBar
3989  * @static
3990  * @type Number
3991  */
3992 Roo.SplitBar.VERTICAL = 1;
3993
3994 /**
3995  * Orientation constant - Create a horizontal SplitBar
3996  * @static
3997  * @type Number
3998  */
3999 Roo.SplitBar.HORIZONTAL = 2;
4000
4001 /**
4002  * Placement constant - The resizing element is to the left of the splitter element
4003  * @static
4004  * @type Number
4005  */
4006 Roo.SplitBar.LEFT = 1;
4007
4008 /**
4009  * Placement constant - The resizing element is to the right of the splitter element
4010  * @static
4011  * @type Number
4012  */
4013 Roo.SplitBar.RIGHT = 2;
4014
4015 /**
4016  * Placement constant - The resizing element is positioned above the splitter element
4017  * @static
4018  * @type Number
4019  */
4020 Roo.SplitBar.TOP = 3;
4021
4022 /**
4023  * Placement constant - The resizing element is positioned under splitter element
4024  * @static
4025  * @type Number
4026  */
4027 Roo.SplitBar.BOTTOM = 4;
4028 /*
4029  * Based on:
4030  * Ext JS Library 1.1.1
4031  * Copyright(c) 2006-2007, Ext JS, LLC.
4032  *
4033  * Originally Released Under LGPL - original licence link has changed is not relivant.
4034  *
4035  * Fork - LGPL
4036  * <script type="text/javascript">
4037  */
4038
4039 /**
4040  * @class Roo.View
4041  * @extends Roo.util.Observable
4042  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
4043  * This class also supports single and multi selection modes. <br>
4044  * Create a data model bound view:
4045  <pre><code>
4046  var store = new Roo.data.Store(...);
4047
4048  var view = new Roo.View({
4049     el : "my-element",
4050     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
4051  
4052     singleSelect: true,
4053     selectedClass: "ydataview-selected",
4054     store: store
4055  });
4056
4057  // listen for node click?
4058  view.on("click", function(vw, index, node, e){
4059  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
4060  });
4061
4062  // load XML data
4063  dataModel.load("foobar.xml");
4064  </code></pre>
4065  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
4066  * <br><br>
4067  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
4068  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
4069  * 
4070  * Note: old style constructor is still suported (container, template, config)
4071  * 
4072  * @constructor
4073  * Create a new View
4074  * @param {Object} config The config object
4075  * 
4076  */
4077 Roo.View = function(config, depreciated_tpl, depreciated_config){
4078     
4079     this.parent = false;
4080     
4081     if (typeof(depreciated_tpl) == 'undefined') {
4082         // new way.. - universal constructor.
4083         Roo.apply(this, config);
4084         this.el  = Roo.get(this.el);
4085     } else {
4086         // old format..
4087         this.el  = Roo.get(config);
4088         this.tpl = depreciated_tpl;
4089         Roo.apply(this, depreciated_config);
4090     }
4091     this.wrapEl  = this.el.wrap().wrap();
4092     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
4093     
4094     
4095     if(typeof(this.tpl) == "string"){
4096         this.tpl = new Roo.Template(this.tpl);
4097     } else {
4098         // support xtype ctors..
4099         this.tpl = new Roo.factory(this.tpl, Roo);
4100     }
4101     
4102     
4103     this.tpl.compile();
4104     
4105     /** @private */
4106     this.addEvents({
4107         /**
4108          * @event beforeclick
4109          * Fires before a click is processed. Returns false to cancel the default action.
4110          * @param {Roo.View} this
4111          * @param {Number} index The index of the target node
4112          * @param {HTMLElement} node The target node
4113          * @param {Roo.EventObject} e The raw event object
4114          */
4115             "beforeclick" : true,
4116         /**
4117          * @event click
4118          * Fires when a template node is clicked.
4119          * @param {Roo.View} this
4120          * @param {Number} index The index of the target node
4121          * @param {HTMLElement} node The target node
4122          * @param {Roo.EventObject} e The raw event object
4123          */
4124             "click" : true,
4125         /**
4126          * @event dblclick
4127          * Fires when a template node is double clicked.
4128          * @param {Roo.View} this
4129          * @param {Number} index The index of the target node
4130          * @param {HTMLElement} node The target node
4131          * @param {Roo.EventObject} e The raw event object
4132          */
4133             "dblclick" : true,
4134         /**
4135          * @event contextmenu
4136          * Fires when a template node is right clicked.
4137          * @param {Roo.View} this
4138          * @param {Number} index The index of the target node
4139          * @param {HTMLElement} node The target node
4140          * @param {Roo.EventObject} e The raw event object
4141          */
4142             "contextmenu" : true,
4143         /**
4144          * @event selectionchange
4145          * Fires when the selected nodes change.
4146          * @param {Roo.View} this
4147          * @param {Array} selections Array of the selected nodes
4148          */
4149             "selectionchange" : true,
4150     
4151         /**
4152          * @event beforeselect
4153          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
4154          * @param {Roo.View} this
4155          * @param {HTMLElement} node The node to be selected
4156          * @param {Array} selections Array of currently selected nodes
4157          */
4158             "beforeselect" : true,
4159         /**
4160          * @event preparedata
4161          * Fires on every row to render, to allow you to change the data.
4162          * @param {Roo.View} this
4163          * @param {Object} data to be rendered (change this)
4164          */
4165           "preparedata" : true
4166           
4167           
4168         });
4169
4170
4171
4172     this.el.on({
4173         "click": this.onClick,
4174         "dblclick": this.onDblClick,
4175         "contextmenu": this.onContextMenu,
4176         scope:this
4177     });
4178
4179     this.selections = [];
4180     this.nodes = [];
4181     this.cmp = new Roo.CompositeElementLite([]);
4182     if(this.store){
4183         this.store = Roo.factory(this.store, Roo.data);
4184         this.setStore(this.store, true);
4185     }
4186     
4187     if ( this.footer && this.footer.xtype) {
4188            
4189          var fctr = this.wrapEl.appendChild(document.createElement("div"));
4190         
4191         this.footer.dataSource = this.store;
4192         this.footer.container = fctr;
4193         this.footer = Roo.factory(this.footer, Roo);
4194         fctr.insertFirst(this.el);
4195         
4196         // this is a bit insane - as the paging toolbar seems to detach the el..
4197 //        dom.parentNode.parentNode.parentNode
4198          // they get detached?
4199     }
4200     
4201     
4202     Roo.View.superclass.constructor.call(this);
4203     
4204     
4205 };
4206
4207 Roo.extend(Roo.View, Roo.util.Observable, {
4208     
4209      /**
4210      * @cfg {Roo.data.Store} store Data store to load data from.
4211      */
4212     store : false,
4213     
4214     /**
4215      * @cfg {String|Roo.Element} el The container element.
4216      */
4217     el : '',
4218     
4219     /**
4220      * @cfg {String|Roo.Template} tpl The template used by this View 
4221      */
4222     tpl : false,
4223     /**
4224      * @cfg {String} dataName the named area of the template to use as the data area
4225      *                          Works with domtemplates roo-name="name"
4226      */
4227     dataName: false,
4228     /**
4229      * @cfg {String} selectedClass The css class to add to selected nodes
4230      */
4231     selectedClass : "x-view-selected",
4232      /**
4233      * @cfg {String} emptyText The empty text to show when nothing is loaded.
4234      */
4235     emptyText : "",
4236     
4237     /**
4238      * @cfg {String} text to display on mask (default Loading)
4239      */
4240     mask : false,
4241     /**
4242      * @cfg {Boolean} multiSelect Allow multiple selection
4243      */
4244     multiSelect : false,
4245     /**
4246      * @cfg {Boolean} singleSelect Allow single selection
4247      */
4248     singleSelect:  false,
4249     
4250     /**
4251      * @cfg {Boolean} toggleSelect - selecting 
4252      */
4253     toggleSelect : false,
4254     
4255     /**
4256      * @cfg {Boolean} tickable - selecting 
4257      */
4258     tickable : false,
4259     
4260     /**
4261      * Returns the element this view is bound to.
4262      * @return {Roo.Element}
4263      */
4264     getEl : function(){
4265         return this.wrapEl;
4266     },
4267     
4268     
4269
4270     /**
4271      * Refreshes the view. - called by datachanged on the store. - do not call directly.
4272      */
4273     refresh : function(){
4274         //Roo.log('refresh');
4275         var t = this.tpl;
4276         
4277         // if we are using something like 'domtemplate', then
4278         // the what gets used is:
4279         // t.applySubtemplate(NAME, data, wrapping data..)
4280         // the outer template then get' applied with
4281         //     the store 'extra data'
4282         // and the body get's added to the
4283         //      roo-name="data" node?
4284         //      <span class='roo-tpl-{name}'></span> ?????
4285         
4286         
4287         
4288         this.clearSelections();
4289         this.el.update("");
4290         var html = [];
4291         var records = this.store.getRange();
4292         if(records.length < 1) {
4293             
4294             // is this valid??  = should it render a template??
4295             
4296             this.el.update(this.emptyText);
4297             return;
4298         }
4299         var el = this.el;
4300         if (this.dataName) {
4301             this.el.update(t.apply(this.store.meta)); //????
4302             el = this.el.child('.roo-tpl-' + this.dataName);
4303         }
4304         
4305         for(var i = 0, len = records.length; i < len; i++){
4306             var data = this.prepareData(records[i].data, i, records[i]);
4307             this.fireEvent("preparedata", this, data, i, records[i]);
4308             
4309             var d = Roo.apply({}, data);
4310             
4311             if(this.tickable){
4312                 Roo.apply(d, {'roo-id' : Roo.id()});
4313                 
4314                 var _this = this;
4315             
4316                 Roo.each(this.parent.item, function(item){
4317                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
4318                         return;
4319                     }
4320                     Roo.apply(d, {'roo-data-checked' : 'checked'});
4321                 });
4322             }
4323             
4324             html[html.length] = Roo.util.Format.trim(
4325                 this.dataName ?
4326                     t.applySubtemplate(this.dataName, d, this.store.meta) :
4327                     t.apply(d)
4328             );
4329         }
4330         
4331         
4332         
4333         el.update(html.join(""));
4334         this.nodes = el.dom.childNodes;
4335         this.updateIndexes(0);
4336     },
4337     
4338
4339     /**
4340      * Function to override to reformat the data that is sent to
4341      * the template for each node.
4342      * DEPRICATED - use the preparedata event handler.
4343      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
4344      * a JSON object for an UpdateManager bound view).
4345      */
4346     prepareData : function(data, index, record)
4347     {
4348         this.fireEvent("preparedata", this, data, index, record);
4349         return data;
4350     },
4351
4352     onUpdate : function(ds, record){
4353         // Roo.log('on update');   
4354         this.clearSelections();
4355         var index = this.store.indexOf(record);
4356         var n = this.nodes[index];
4357         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
4358         n.parentNode.removeChild(n);
4359         this.updateIndexes(index, index);
4360     },
4361
4362     
4363     
4364 // --------- FIXME     
4365     onAdd : function(ds, records, index)
4366     {
4367         //Roo.log(['on Add', ds, records, index] );        
4368         this.clearSelections();
4369         if(this.nodes.length == 0){
4370             this.refresh();
4371             return;
4372         }
4373         var n = this.nodes[index];
4374         for(var i = 0, len = records.length; i < len; i++){
4375             var d = this.prepareData(records[i].data, i, records[i]);
4376             if(n){
4377                 this.tpl.insertBefore(n, d);
4378             }else{
4379                 
4380                 this.tpl.append(this.el, d);
4381             }
4382         }
4383         this.updateIndexes(index);
4384     },
4385
4386     onRemove : function(ds, record, index){
4387        // Roo.log('onRemove');
4388         this.clearSelections();
4389         var el = this.dataName  ?
4390             this.el.child('.roo-tpl-' + this.dataName) :
4391             this.el; 
4392         
4393         el.dom.removeChild(this.nodes[index]);
4394         this.updateIndexes(index);
4395     },
4396
4397     /**
4398      * Refresh an individual node.
4399      * @param {Number} index
4400      */
4401     refreshNode : function(index){
4402         this.onUpdate(this.store, this.store.getAt(index));
4403     },
4404
4405     updateIndexes : function(startIndex, endIndex){
4406         var ns = this.nodes;
4407         startIndex = startIndex || 0;
4408         endIndex = endIndex || ns.length - 1;
4409         for(var i = startIndex; i <= endIndex; i++){
4410             ns[i].nodeIndex = i;
4411         }
4412     },
4413
4414     /**
4415      * Changes the data store this view uses and refresh the view.
4416      * @param {Store} store
4417      */
4418     setStore : function(store, initial){
4419         if(!initial && this.store){
4420             this.store.un("datachanged", this.refresh);
4421             this.store.un("add", this.onAdd);
4422             this.store.un("remove", this.onRemove);
4423             this.store.un("update", this.onUpdate);
4424             this.store.un("clear", this.refresh);
4425             this.store.un("beforeload", this.onBeforeLoad);
4426             this.store.un("load", this.onLoad);
4427             this.store.un("loadexception", this.onLoad);
4428         }
4429         if(store){
4430           
4431             store.on("datachanged", this.refresh, this);
4432             store.on("add", this.onAdd, this);
4433             store.on("remove", this.onRemove, this);
4434             store.on("update", this.onUpdate, this);
4435             store.on("clear", this.refresh, this);
4436             store.on("beforeload", this.onBeforeLoad, this);
4437             store.on("load", this.onLoad, this);
4438             store.on("loadexception", this.onLoad, this);
4439         }
4440         
4441         if(store){
4442             this.refresh();
4443         }
4444     },
4445     /**
4446      * onbeforeLoad - masks the loading area.
4447      *
4448      */
4449     onBeforeLoad : function(store,opts)
4450     {
4451          //Roo.log('onBeforeLoad');   
4452         if (!opts.add) {
4453             this.el.update("");
4454         }
4455         this.el.mask(this.mask ? this.mask : "Loading" ); 
4456     },
4457     onLoad : function ()
4458     {
4459         this.el.unmask();
4460     },
4461     
4462
4463     /**
4464      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
4465      * @param {HTMLElement} node
4466      * @return {HTMLElement} The template node
4467      */
4468     findItemFromChild : function(node){
4469         var el = this.dataName  ?
4470             this.el.child('.roo-tpl-' + this.dataName,true) :
4471             this.el.dom; 
4472         
4473         if(!node || node.parentNode == el){
4474                     return node;
4475             }
4476             var p = node.parentNode;
4477             while(p && p != el){
4478             if(p.parentNode == el){
4479                 return p;
4480             }
4481             p = p.parentNode;
4482         }
4483             return null;
4484     },
4485
4486     /** @ignore */
4487     onClick : function(e){
4488         var item = this.findItemFromChild(e.getTarget());
4489         if(item){
4490             var index = this.indexOf(item);
4491             if(this.onItemClick(item, index, e) !== false){
4492                 this.fireEvent("click", this, index, item, e);
4493             }
4494         }else{
4495             this.clearSelections();
4496         }
4497     },
4498
4499     /** @ignore */
4500     onContextMenu : function(e){
4501         var item = this.findItemFromChild(e.getTarget());
4502         if(item){
4503             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
4504         }
4505     },
4506
4507     /** @ignore */
4508     onDblClick : function(e){
4509         var item = this.findItemFromChild(e.getTarget());
4510         if(item){
4511             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
4512         }
4513     },
4514
4515     onItemClick : function(item, index, e)
4516     {
4517         if(this.fireEvent("beforeclick", this, index, item, e) === false){
4518             return false;
4519         }
4520         if (this.toggleSelect) {
4521             var m = this.isSelected(item) ? 'unselect' : 'select';
4522             //Roo.log(m);
4523             var _t = this;
4524             _t[m](item, true, false);
4525             return true;
4526         }
4527         if(this.multiSelect || this.singleSelect){
4528             if(this.multiSelect && e.shiftKey && this.lastSelection){
4529                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
4530             }else{
4531                 this.select(item, this.multiSelect && e.ctrlKey);
4532                 this.lastSelection = item;
4533             }
4534             
4535             if(!this.tickable){
4536                 e.preventDefault();
4537             }
4538             
4539         }
4540         return true;
4541     },
4542
4543     /**
4544      * Get the number of selected nodes.
4545      * @return {Number}
4546      */
4547     getSelectionCount : function(){
4548         return this.selections.length;
4549     },
4550
4551     /**
4552      * Get the currently selected nodes.
4553      * @return {Array} An array of HTMLElements
4554      */
4555     getSelectedNodes : function(){
4556         return this.selections;
4557     },
4558
4559     /**
4560      * Get the indexes of the selected nodes.
4561      * @return {Array}
4562      */
4563     getSelectedIndexes : function(){
4564         var indexes = [], s = this.selections;
4565         for(var i = 0, len = s.length; i < len; i++){
4566             indexes.push(s[i].nodeIndex);
4567         }
4568         return indexes;
4569     },
4570
4571     /**
4572      * Clear all selections
4573      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
4574      */
4575     clearSelections : function(suppressEvent){
4576         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
4577             this.cmp.elements = this.selections;
4578             this.cmp.removeClass(this.selectedClass);
4579             this.selections = [];
4580             if(!suppressEvent){
4581                 this.fireEvent("selectionchange", this, this.selections);
4582             }
4583         }
4584     },
4585
4586     /**
4587      * Returns true if the passed node is selected
4588      * @param {HTMLElement/Number} node The node or node index
4589      * @return {Boolean}
4590      */
4591     isSelected : function(node){
4592         var s = this.selections;
4593         if(s.length < 1){
4594             return false;
4595         }
4596         node = this.getNode(node);
4597         return s.indexOf(node) !== -1;
4598     },
4599
4600     /**
4601      * Selects nodes.
4602      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
4603      * @param {Boolean} keepExisting (optional) true to keep existing selections
4604      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4605      */
4606     select : function(nodeInfo, keepExisting, suppressEvent){
4607         if(nodeInfo instanceof Array){
4608             if(!keepExisting){
4609                 this.clearSelections(true);
4610             }
4611             for(var i = 0, len = nodeInfo.length; i < len; i++){
4612                 this.select(nodeInfo[i], true, true);
4613             }
4614             return;
4615         } 
4616         var node = this.getNode(nodeInfo);
4617         if(!node || this.isSelected(node)){
4618             return; // already selected.
4619         }
4620         if(!keepExisting){
4621             this.clearSelections(true);
4622         }
4623         
4624         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
4625             Roo.fly(node).addClass(this.selectedClass);
4626             this.selections.push(node);
4627             if(!suppressEvent){
4628                 this.fireEvent("selectionchange", this, this.selections);
4629             }
4630         }
4631         
4632         
4633     },
4634       /**
4635      * Unselects nodes.
4636      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
4637      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
4638      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4639      */
4640     unselect : function(nodeInfo, keepExisting, suppressEvent)
4641     {
4642         if(nodeInfo instanceof Array){
4643             Roo.each(this.selections, function(s) {
4644                 this.unselect(s, nodeInfo);
4645             }, this);
4646             return;
4647         }
4648         var node = this.getNode(nodeInfo);
4649         if(!node || !this.isSelected(node)){
4650             //Roo.log("not selected");
4651             return; // not selected.
4652         }
4653         // fireevent???
4654         var ns = [];
4655         Roo.each(this.selections, function(s) {
4656             if (s == node ) {
4657                 Roo.fly(node).removeClass(this.selectedClass);
4658
4659                 return;
4660             }
4661             ns.push(s);
4662         },this);
4663         
4664         this.selections= ns;
4665         this.fireEvent("selectionchange", this, this.selections);
4666     },
4667
4668     /**
4669      * Gets a template node.
4670      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4671      * @return {HTMLElement} The node or null if it wasn't found
4672      */
4673     getNode : function(nodeInfo){
4674         if(typeof nodeInfo == "string"){
4675             return document.getElementById(nodeInfo);
4676         }else if(typeof nodeInfo == "number"){
4677             return this.nodes[nodeInfo];
4678         }
4679         return nodeInfo;
4680     },
4681
4682     /**
4683      * Gets a range template nodes.
4684      * @param {Number} startIndex
4685      * @param {Number} endIndex
4686      * @return {Array} An array of nodes
4687      */
4688     getNodes : function(start, end){
4689         var ns = this.nodes;
4690         start = start || 0;
4691         end = typeof end == "undefined" ? ns.length - 1 : end;
4692         var nodes = [];
4693         if(start <= end){
4694             for(var i = start; i <= end; i++){
4695                 nodes.push(ns[i]);
4696             }
4697         } else{
4698             for(var i = start; i >= end; i--){
4699                 nodes.push(ns[i]);
4700             }
4701         }
4702         return nodes;
4703     },
4704
4705     /**
4706      * Finds the index of the passed node
4707      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4708      * @return {Number} The index of the node or -1
4709      */
4710     indexOf : function(node){
4711         node = this.getNode(node);
4712         if(typeof node.nodeIndex == "number"){
4713             return node.nodeIndex;
4714         }
4715         var ns = this.nodes;
4716         for(var i = 0, len = ns.length; i < len; i++){
4717             if(ns[i] == node){
4718                 return i;
4719             }
4720         }
4721         return -1;
4722     }
4723 });
4724 /*
4725  * Based on:
4726  * Ext JS Library 1.1.1
4727  * Copyright(c) 2006-2007, Ext JS, LLC.
4728  *
4729  * Originally Released Under LGPL - original licence link has changed is not relivant.
4730  *
4731  * Fork - LGPL
4732  * <script type="text/javascript">
4733  */
4734
4735 /**
4736  * @class Roo.JsonView
4737  * @extends Roo.View
4738  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
4739 <pre><code>
4740 var view = new Roo.JsonView({
4741     container: "my-element",
4742     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
4743     multiSelect: true, 
4744     jsonRoot: "data" 
4745 });
4746
4747 // listen for node click?
4748 view.on("click", function(vw, index, node, e){
4749     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
4750 });
4751
4752 // direct load of JSON data
4753 view.load("foobar.php");
4754
4755 // Example from my blog list
4756 var tpl = new Roo.Template(
4757     '&lt;div class="entry"&gt;' +
4758     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
4759     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
4760     "&lt;/div&gt;&lt;hr /&gt;"
4761 );
4762
4763 var moreView = new Roo.JsonView({
4764     container :  "entry-list", 
4765     template : tpl,
4766     jsonRoot: "posts"
4767 });
4768 moreView.on("beforerender", this.sortEntries, this);
4769 moreView.load({
4770     url: "/blog/get-posts.php",
4771     params: "allposts=true",
4772     text: "Loading Blog Entries..."
4773 });
4774 </code></pre>
4775
4776 * Note: old code is supported with arguments : (container, template, config)
4777
4778
4779  * @constructor
4780  * Create a new JsonView
4781  * 
4782  * @param {Object} config The config object
4783  * 
4784  */
4785 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
4786     
4787     
4788     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
4789
4790     var um = this.el.getUpdateManager();
4791     um.setRenderer(this);
4792     um.on("update", this.onLoad, this);
4793     um.on("failure", this.onLoadException, this);
4794
4795     /**
4796      * @event beforerender
4797      * Fires before rendering of the downloaded JSON data.
4798      * @param {Roo.JsonView} this
4799      * @param {Object} data The JSON data loaded
4800      */
4801     /**
4802      * @event load
4803      * Fires when data is loaded.
4804      * @param {Roo.JsonView} this
4805      * @param {Object} data The JSON data loaded
4806      * @param {Object} response The raw Connect response object
4807      */
4808     /**
4809      * @event loadexception
4810      * Fires when loading fails.
4811      * @param {Roo.JsonView} this
4812      * @param {Object} response The raw Connect response object
4813      */
4814     this.addEvents({
4815         'beforerender' : true,
4816         'load' : true,
4817         'loadexception' : true
4818     });
4819 };
4820 Roo.extend(Roo.JsonView, Roo.View, {
4821     /**
4822      * @type {String} The root property in the loaded JSON object that contains the data
4823      */
4824     jsonRoot : "",
4825
4826     /**
4827      * Refreshes the view.
4828      */
4829     refresh : function(){
4830         this.clearSelections();
4831         this.el.update("");
4832         var html = [];
4833         var o = this.jsonData;
4834         if(o && o.length > 0){
4835             for(var i = 0, len = o.length; i < len; i++){
4836                 var data = this.prepareData(o[i], i, o);
4837                 html[html.length] = this.tpl.apply(data);
4838             }
4839         }else{
4840             html.push(this.emptyText);
4841         }
4842         this.el.update(html.join(""));
4843         this.nodes = this.el.dom.childNodes;
4844         this.updateIndexes(0);
4845     },
4846
4847     /**
4848      * Performs an async HTTP request, and loads the JSON from the response. If <i>params</i> are specified it uses POST, otherwise it uses GET.
4849      * @param {Object/String/Function} url The URL for this request, or a function to call to get the URL, or a config object containing any of the following options:
4850      <pre><code>
4851      view.load({
4852          url: "your-url.php",
4853          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
4854          callback: yourFunction,
4855          scope: yourObject, //(optional scope)
4856          discardUrl: false,
4857          nocache: false,
4858          text: "Loading...",
4859          timeout: 30,
4860          scripts: false
4861      });
4862      </code></pre>
4863      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
4864      * are respectively shorthand for <i>disableCaching</i>, <i>indicatorText</i>, and <i>loadScripts</i> and are used to set their associated property on this UpdateManager instance.
4865      * @param {String/Object} params (optional) The parameters to pass, as either a URL encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
4866      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
4867      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used URL. If true, it will not store the URL.
4868      */
4869     load : function(){
4870         var um = this.el.getUpdateManager();
4871         um.update.apply(um, arguments);
4872     },
4873
4874     // note - render is a standard framework call...
4875     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
4876     render : function(el, response){
4877         
4878         this.clearSelections();
4879         this.el.update("");
4880         var o;
4881         try{
4882             if (response != '') {
4883                 o = Roo.util.JSON.decode(response.responseText);
4884                 if(this.jsonRoot){
4885                     
4886                     o = o[this.jsonRoot];
4887                 }
4888             }
4889         } catch(e){
4890         }
4891         /**
4892          * The current JSON data or null
4893          */
4894         this.jsonData = o;
4895         this.beforeRender();
4896         this.refresh();
4897     },
4898
4899 /**
4900  * Get the number of records in the current JSON dataset
4901  * @return {Number}
4902  */
4903     getCount : function(){
4904         return this.jsonData ? this.jsonData.length : 0;
4905     },
4906
4907 /**
4908  * Returns the JSON object for the specified node(s)
4909  * @param {HTMLElement/Array} node The node or an array of nodes
4910  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
4911  * you get the JSON object for the node
4912  */
4913     getNodeData : function(node){
4914         if(node instanceof Array){
4915             var data = [];
4916             for(var i = 0, len = node.length; i < len; i++){
4917                 data.push(this.getNodeData(node[i]));
4918             }
4919             return data;
4920         }
4921         return this.jsonData[this.indexOf(node)] || null;
4922     },
4923
4924     beforeRender : function(){
4925         this.snapshot = this.jsonData;
4926         if(this.sortInfo){
4927             this.sort.apply(this, this.sortInfo);
4928         }
4929         this.fireEvent("beforerender", this, this.jsonData);
4930     },
4931
4932     onLoad : function(el, o){
4933         this.fireEvent("load", this, this.jsonData, o);
4934     },
4935
4936     onLoadException : function(el, o){
4937         this.fireEvent("loadexception", this, o);
4938     },
4939
4940 /**
4941  * Filter the data by a specific property.
4942  * @param {String} property A property on your JSON objects
4943  * @param {String/RegExp} value Either string that the property values
4944  * should start with, or a RegExp to test against the property
4945  */
4946     filter : function(property, value){
4947         if(this.jsonData){
4948             var data = [];
4949             var ss = this.snapshot;
4950             if(typeof value == "string"){
4951                 var vlen = value.length;
4952                 if(vlen == 0){
4953                     this.clearFilter();
4954                     return;
4955                 }
4956                 value = value.toLowerCase();
4957                 for(var i = 0, len = ss.length; i < len; i++){
4958                     var o = ss[i];
4959                     if(o[property].substr(0, vlen).toLowerCase() == value){
4960                         data.push(o);
4961                     }
4962                 }
4963             } else if(value.exec){ // regex?
4964                 for(var i = 0, len = ss.length; i < len; i++){
4965                     var o = ss[i];
4966                     if(value.test(o[property])){
4967                         data.push(o);
4968                     }
4969                 }
4970             } else{
4971                 return;
4972             }
4973             this.jsonData = data;
4974             this.refresh();
4975         }
4976     },
4977
4978 /**
4979  * Filter by a function. The passed function will be called with each
4980  * object in the current dataset. If the function returns true the value is kept,
4981  * otherwise it is filtered.
4982  * @param {Function} fn
4983  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
4984  */
4985     filterBy : function(fn, scope){
4986         if(this.jsonData){
4987             var data = [];
4988             var ss = this.snapshot;
4989             for(var i = 0, len = ss.length; i < len; i++){
4990                 var o = ss[i];
4991                 if(fn.call(scope || this, o)){
4992                     data.push(o);
4993                 }
4994             }
4995             this.jsonData = data;
4996             this.refresh();
4997         }
4998     },
4999
5000 /**
5001  * Clears the current filter.
5002  */
5003     clearFilter : function(){
5004         if(this.snapshot && this.jsonData != this.snapshot){
5005             this.jsonData = this.snapshot;
5006             this.refresh();
5007         }
5008     },
5009
5010
5011 /**
5012  * Sorts the data for this view and refreshes it.
5013  * @param {String} property A property on your JSON objects to sort on
5014  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
5015  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
5016  */
5017     sort : function(property, dir, sortType){
5018         this.sortInfo = Array.prototype.slice.call(arguments, 0);
5019         if(this.jsonData){
5020             var p = property;
5021             var dsc = dir && dir.toLowerCase() == "desc";
5022             var f = function(o1, o2){
5023                 var v1 = sortType ? sortType(o1[p]) : o1[p];
5024                 var v2 = sortType ? sortType(o2[p]) : o2[p];
5025                 ;
5026                 if(v1 < v2){
5027                     return dsc ? +1 : -1;
5028                 } else if(v1 > v2){
5029                     return dsc ? -1 : +1;
5030                 } else{
5031                     return 0;
5032                 }
5033             };
5034             this.jsonData.sort(f);
5035             this.refresh();
5036             if(this.jsonData != this.snapshot){
5037                 this.snapshot.sort(f);
5038             }
5039         }
5040     }
5041 });/*
5042  * Based on:
5043  * Ext JS Library 1.1.1
5044  * Copyright(c) 2006-2007, Ext JS, LLC.
5045  *
5046  * Originally Released Under LGPL - original licence link has changed is not relivant.
5047  *
5048  * Fork - LGPL
5049  * <script type="text/javascript">
5050  */
5051  
5052
5053 /**
5054  * @class Roo.ColorPalette
5055  * @extends Roo.Component
5056  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
5057  * Here's an example of typical usage:
5058  * <pre><code>
5059 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
5060 cp.render('my-div');
5061
5062 cp.on('select', function(palette, selColor){
5063     // do something with selColor
5064 });
5065 </code></pre>
5066  * @constructor
5067  * Create a new ColorPalette
5068  * @param {Object} config The config object
5069  */
5070 Roo.ColorPalette = function(config){
5071     Roo.ColorPalette.superclass.constructor.call(this, config);
5072     this.addEvents({
5073         /**
5074              * @event select
5075              * Fires when a color is selected
5076              * @param {ColorPalette} this
5077              * @param {String} color The 6-digit color hex code (without the # symbol)
5078              */
5079         select: true
5080     });
5081
5082     if(this.handler){
5083         this.on("select", this.handler, this.scope, true);
5084     }
5085 };
5086 Roo.extend(Roo.ColorPalette, Roo.Component, {
5087     /**
5088      * @cfg {String} itemCls
5089      * The CSS class to apply to the containing element (defaults to "x-color-palette")
5090      */
5091     itemCls : "x-color-palette",
5092     /**
5093      * @cfg {String} value
5094      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
5095      * the hex codes are case-sensitive.
5096      */
5097     value : null,
5098     clickEvent:'click',
5099     // private
5100     ctype: "Roo.ColorPalette",
5101
5102     /**
5103      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
5104      */
5105     allowReselect : false,
5106
5107     /**
5108      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
5109      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
5110      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
5111      * of colors with the width setting until the box is symmetrical.</p>
5112      * <p>You can override individual colors if needed:</p>
5113      * <pre><code>
5114 var cp = new Roo.ColorPalette();
5115 cp.colors[0] = "FF0000";  // change the first box to red
5116 </code></pre>
5117
5118 Or you can provide a custom array of your own for complete control:
5119 <pre><code>
5120 var cp = new Roo.ColorPalette();
5121 cp.colors = ["000000", "993300", "333300"];
5122 </code></pre>
5123      * @type Array
5124      */
5125     colors : [
5126         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
5127         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
5128         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
5129         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
5130         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
5131     ],
5132
5133     // private
5134     onRender : function(container, position){
5135         var t = new Roo.MasterTemplate(
5136             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
5137         );
5138         var c = this.colors;
5139         for(var i = 0, len = c.length; i < len; i++){
5140             t.add([c[i]]);
5141         }
5142         var el = document.createElement("div");
5143         el.className = this.itemCls;
5144         t.overwrite(el);
5145         container.dom.insertBefore(el, position);
5146         this.el = Roo.get(el);
5147         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
5148         if(this.clickEvent != 'click'){
5149             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
5150         }
5151     },
5152
5153     // private
5154     afterRender : function(){
5155         Roo.ColorPalette.superclass.afterRender.call(this);
5156         if(this.value){
5157             var s = this.value;
5158             this.value = null;
5159             this.select(s);
5160         }
5161     },
5162
5163     // private
5164     handleClick : function(e, t){
5165         e.preventDefault();
5166         if(!this.disabled){
5167             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
5168             this.select(c.toUpperCase());
5169         }
5170     },
5171
5172     /**
5173      * Selects the specified color in the palette (fires the select event)
5174      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
5175      */
5176     select : function(color){
5177         color = color.replace("#", "");
5178         if(color != this.value || this.allowReselect){
5179             var el = this.el;
5180             if(this.value){
5181                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
5182             }
5183             el.child("a.color-"+color).addClass("x-color-palette-sel");
5184             this.value = color;
5185             this.fireEvent("select", this, color);
5186         }
5187     }
5188 });/*
5189  * Based on:
5190  * Ext JS Library 1.1.1
5191  * Copyright(c) 2006-2007, Ext JS, LLC.
5192  *
5193  * Originally Released Under LGPL - original licence link has changed is not relivant.
5194  *
5195  * Fork - LGPL
5196  * <script type="text/javascript">
5197  */
5198  
5199 /**
5200  * @class Roo.DatePicker
5201  * @extends Roo.Component
5202  * Simple date picker class.
5203  * @constructor
5204  * Create a new DatePicker
5205  * @param {Object} config The config object
5206  */
5207 Roo.DatePicker = function(config){
5208     Roo.DatePicker.superclass.constructor.call(this, config);
5209
5210     this.value = config && config.value ?
5211                  config.value.clearTime() : new Date().clearTime();
5212
5213     this.addEvents({
5214         /**
5215              * @event select
5216              * Fires when a date is selected
5217              * @param {DatePicker} this
5218              * @param {Date} date The selected date
5219              */
5220         'select': true,
5221         /**
5222              * @event monthchange
5223              * Fires when the displayed month changes 
5224              * @param {DatePicker} this
5225              * @param {Date} date The selected month
5226              */
5227         'monthchange': true
5228     });
5229
5230     if(this.handler){
5231         this.on("select", this.handler,  this.scope || this);
5232     }
5233     // build the disabledDatesRE
5234     if(!this.disabledDatesRE && this.disabledDates){
5235         var dd = this.disabledDates;
5236         var re = "(?:";
5237         for(var i = 0; i < dd.length; i++){
5238             re += dd[i];
5239             if(i != dd.length-1) {
5240                 re += "|";
5241             }
5242         }
5243         this.disabledDatesRE = new RegExp(re + ")");
5244     }
5245 };
5246
5247 Roo.extend(Roo.DatePicker, Roo.Component, {
5248     /**
5249      * @cfg {String} todayText
5250      * The text to display on the button that selects the current date (defaults to "Today")
5251      */
5252     todayText : "Today",
5253     /**
5254      * @cfg {String} okText
5255      * The text to display on the ok button
5256      */
5257     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
5258     /**
5259      * @cfg {String} cancelText
5260      * The text to display on the cancel button
5261      */
5262     cancelText : "Cancel",
5263     /**
5264      * @cfg {String} todayTip
5265      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
5266      */
5267     todayTip : "{0} (Spacebar)",
5268     /**
5269      * @cfg {Date} minDate
5270      * Minimum allowable date (JavaScript date object, defaults to null)
5271      */
5272     minDate : null,
5273     /**
5274      * @cfg {Date} maxDate
5275      * Maximum allowable date (JavaScript date object, defaults to null)
5276      */
5277     maxDate : null,
5278     /**
5279      * @cfg {String} minText
5280      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
5281      */
5282     minText : "This date is before the minimum date",
5283     /**
5284      * @cfg {String} maxText
5285      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
5286      */
5287     maxText : "This date is after the maximum date",
5288     /**
5289      * @cfg {String} format
5290      * The default date format string which can be overriden for localization support.  The format must be
5291      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
5292      */
5293     format : "m/d/y",
5294     /**
5295      * @cfg {Array} disabledDays
5296      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
5297      */
5298     disabledDays : null,
5299     /**
5300      * @cfg {String} disabledDaysText
5301      * The tooltip to display when the date falls on a disabled day (defaults to "")
5302      */
5303     disabledDaysText : "",
5304     /**
5305      * @cfg {RegExp} disabledDatesRE
5306      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
5307      */
5308     disabledDatesRE : null,
5309     /**
5310      * @cfg {String} disabledDatesText
5311      * The tooltip text to display when the date falls on a disabled date (defaults to "")
5312      */
5313     disabledDatesText : "",
5314     /**
5315      * @cfg {Boolean} constrainToViewport
5316      * True to constrain the date picker to the viewport (defaults to true)
5317      */
5318     constrainToViewport : true,
5319     /**
5320      * @cfg {Array} monthNames
5321      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
5322      */
5323     monthNames : Date.monthNames,
5324     /**
5325      * @cfg {Array} dayNames
5326      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
5327      */
5328     dayNames : Date.dayNames,
5329     /**
5330      * @cfg {String} nextText
5331      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
5332      */
5333     nextText: 'Next Month (Control+Right)',
5334     /**
5335      * @cfg {String} prevText
5336      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
5337      */
5338     prevText: 'Previous Month (Control+Left)',
5339     /**
5340      * @cfg {String} monthYearText
5341      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
5342      */
5343     monthYearText: 'Choose a month (Control+Up/Down to move years)',
5344     /**
5345      * @cfg {Number} startDay
5346      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
5347      */
5348     startDay : 0,
5349     /**
5350      * @cfg {Bool} showClear
5351      * Show a clear button (usefull for date form elements that can be blank.)
5352      */
5353     
5354     showClear: false,
5355     
5356     /**
5357      * Sets the value of the date field
5358      * @param {Date} value The date to set
5359      */
5360     setValue : function(value){
5361         var old = this.value;
5362         
5363         if (typeof(value) == 'string') {
5364          
5365             value = Date.parseDate(value, this.format);
5366         }
5367         if (!value) {
5368             value = new Date();
5369         }
5370         
5371         this.value = value.clearTime(true);
5372         if(this.el){
5373             this.update(this.value);
5374         }
5375     },
5376
5377     /**
5378      * Gets the current selected value of the date field
5379      * @return {Date} The selected date
5380      */
5381     getValue : function(){
5382         return this.value;
5383     },
5384
5385     // private
5386     focus : function(){
5387         if(this.el){
5388             this.update(this.activeDate);
5389         }
5390     },
5391
5392     // privateval
5393     onRender : function(container, position){
5394         
5395         var m = [
5396              '<table cellspacing="0">',
5397                 '<tr><td class="x-date-left"><a href="#" title="', this.prevText ,'">&#160;</a></td><td class="x-date-middle" align="center"></td><td class="x-date-right"><a href="#" title="', this.nextText ,'">&#160;</a></td></tr>',
5398                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
5399         var dn = this.dayNames;
5400         for(var i = 0; i < 7; i++){
5401             var d = this.startDay+i;
5402             if(d > 6){
5403                 d = d-7;
5404             }
5405             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
5406         }
5407         m[m.length] = "</tr></thead><tbody><tr>";
5408         for(var i = 0; i < 42; i++) {
5409             if(i % 7 == 0 && i != 0){
5410                 m[m.length] = "</tr><tr>";
5411             }
5412             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
5413         }
5414         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
5415             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
5416
5417         var el = document.createElement("div");
5418         el.className = "x-date-picker";
5419         el.innerHTML = m.join("");
5420
5421         container.dom.insertBefore(el, position);
5422
5423         this.el = Roo.get(el);
5424         this.eventEl = Roo.get(el.firstChild);
5425
5426         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
5427             handler: this.showPrevMonth,
5428             scope: this,
5429             preventDefault:true,
5430             stopDefault:true
5431         });
5432
5433         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
5434             handler: this.showNextMonth,
5435             scope: this,
5436             preventDefault:true,
5437             stopDefault:true
5438         });
5439
5440         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
5441
5442         this.monthPicker = this.el.down('div.x-date-mp');
5443         this.monthPicker.enableDisplayMode('block');
5444         
5445         var kn = new Roo.KeyNav(this.eventEl, {
5446             "left" : function(e){
5447                 e.ctrlKey ?
5448                     this.showPrevMonth() :
5449                     this.update(this.activeDate.add("d", -1));
5450             },
5451
5452             "right" : function(e){
5453                 e.ctrlKey ?
5454                     this.showNextMonth() :
5455                     this.update(this.activeDate.add("d", 1));
5456             },
5457
5458             "up" : function(e){
5459                 e.ctrlKey ?
5460                     this.showNextYear() :
5461                     this.update(this.activeDate.add("d", -7));
5462             },
5463
5464             "down" : function(e){
5465                 e.ctrlKey ?
5466                     this.showPrevYear() :
5467                     this.update(this.activeDate.add("d", 7));
5468             },
5469
5470             "pageUp" : function(e){
5471                 this.showNextMonth();
5472             },
5473
5474             "pageDown" : function(e){
5475                 this.showPrevMonth();
5476             },
5477
5478             "enter" : function(e){
5479                 e.stopPropagation();
5480                 return true;
5481             },
5482
5483             scope : this
5484         });
5485
5486         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
5487
5488         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
5489
5490         this.el.unselectable();
5491         
5492         this.cells = this.el.select("table.x-date-inner tbody td");
5493         this.textNodes = this.el.query("table.x-date-inner tbody span");
5494
5495         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
5496             text: "&#160;",
5497             tooltip: this.monthYearText
5498         });
5499
5500         this.mbtn.on('click', this.showMonthPicker, this);
5501         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
5502
5503
5504         var today = (new Date()).dateFormat(this.format);
5505         
5506         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
5507         if (this.showClear) {
5508             baseTb.add( new Roo.Toolbar.Fill());
5509         }
5510         baseTb.add({
5511             text: String.format(this.todayText, today),
5512             tooltip: String.format(this.todayTip, today),
5513             handler: this.selectToday,
5514             scope: this
5515         });
5516         
5517         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
5518             
5519         //});
5520         if (this.showClear) {
5521             
5522             baseTb.add( new Roo.Toolbar.Fill());
5523             baseTb.add({
5524                 text: '&#160;',
5525                 cls: 'x-btn-icon x-btn-clear',
5526                 handler: function() {
5527                     //this.value = '';
5528                     this.fireEvent("select", this, '');
5529                 },
5530                 scope: this
5531             });
5532         }
5533         
5534         
5535         if(Roo.isIE){
5536             this.el.repaint();
5537         }
5538         this.update(this.value);
5539     },
5540
5541     createMonthPicker : function(){
5542         if(!this.monthPicker.dom.firstChild){
5543             var buf = ['<table border="0" cellspacing="0">'];
5544             for(var i = 0; i < 6; i++){
5545                 buf.push(
5546                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
5547                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
5548                     i == 0 ?
5549                     '<td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-prev"></a></td><td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-next"></a></td></tr>' :
5550                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
5551                 );
5552             }
5553             buf.push(
5554                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
5555                     this.okText,
5556                     '</button><button type="button" class="x-date-mp-cancel">',
5557                     this.cancelText,
5558                     '</button></td></tr>',
5559                 '</table>'
5560             );
5561             this.monthPicker.update(buf.join(''));
5562             this.monthPicker.on('click', this.onMonthClick, this);
5563             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
5564
5565             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
5566             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
5567
5568             this.mpMonths.each(function(m, a, i){
5569                 i += 1;
5570                 if((i%2) == 0){
5571                     m.dom.xmonth = 5 + Math.round(i * .5);
5572                 }else{
5573                     m.dom.xmonth = Math.round((i-1) * .5);
5574                 }
5575             });
5576         }
5577     },
5578
5579     showMonthPicker : function(){
5580         this.createMonthPicker();
5581         var size = this.el.getSize();
5582         this.monthPicker.setSize(size);
5583         this.monthPicker.child('table').setSize(size);
5584
5585         this.mpSelMonth = (this.activeDate || this.value).getMonth();
5586         this.updateMPMonth(this.mpSelMonth);
5587         this.mpSelYear = (this.activeDate || this.value).getFullYear();
5588         this.updateMPYear(this.mpSelYear);
5589
5590         this.monthPicker.slideIn('t', {duration:.2});
5591     },
5592
5593     updateMPYear : function(y){
5594         this.mpyear = y;
5595         var ys = this.mpYears.elements;
5596         for(var i = 1; i <= 10; i++){
5597             var td = ys[i-1], y2;
5598             if((i%2) == 0){
5599                 y2 = y + Math.round(i * .5);
5600                 td.firstChild.innerHTML = y2;
5601                 td.xyear = y2;
5602             }else{
5603                 y2 = y - (5-Math.round(i * .5));
5604                 td.firstChild.innerHTML = y2;
5605                 td.xyear = y2;
5606             }
5607             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
5608         }
5609     },
5610
5611     updateMPMonth : function(sm){
5612         this.mpMonths.each(function(m, a, i){
5613             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
5614         });
5615     },
5616
5617     selectMPMonth: function(m){
5618         
5619     },
5620
5621     onMonthClick : function(e, t){
5622         e.stopEvent();
5623         var el = new Roo.Element(t), pn;
5624         if(el.is('button.x-date-mp-cancel')){
5625             this.hideMonthPicker();
5626         }
5627         else if(el.is('button.x-date-mp-ok')){
5628             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5629             this.hideMonthPicker();
5630         }
5631         else if(pn = el.up('td.x-date-mp-month', 2)){
5632             this.mpMonths.removeClass('x-date-mp-sel');
5633             pn.addClass('x-date-mp-sel');
5634             this.mpSelMonth = pn.dom.xmonth;
5635         }
5636         else if(pn = el.up('td.x-date-mp-year', 2)){
5637             this.mpYears.removeClass('x-date-mp-sel');
5638             pn.addClass('x-date-mp-sel');
5639             this.mpSelYear = pn.dom.xyear;
5640         }
5641         else if(el.is('a.x-date-mp-prev')){
5642             this.updateMPYear(this.mpyear-10);
5643         }
5644         else if(el.is('a.x-date-mp-next')){
5645             this.updateMPYear(this.mpyear+10);
5646         }
5647     },
5648
5649     onMonthDblClick : function(e, t){
5650         e.stopEvent();
5651         var el = new Roo.Element(t), pn;
5652         if(pn = el.up('td.x-date-mp-month', 2)){
5653             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
5654             this.hideMonthPicker();
5655         }
5656         else if(pn = el.up('td.x-date-mp-year', 2)){
5657             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5658             this.hideMonthPicker();
5659         }
5660     },
5661
5662     hideMonthPicker : function(disableAnim){
5663         if(this.monthPicker){
5664             if(disableAnim === true){
5665                 this.monthPicker.hide();
5666             }else{
5667                 this.monthPicker.slideOut('t', {duration:.2});
5668             }
5669         }
5670     },
5671
5672     // private
5673     showPrevMonth : function(e){
5674         this.update(this.activeDate.add("mo", -1));
5675     },
5676
5677     // private
5678     showNextMonth : function(e){
5679         this.update(this.activeDate.add("mo", 1));
5680     },
5681
5682     // private
5683     showPrevYear : function(){
5684         this.update(this.activeDate.add("y", -1));
5685     },
5686
5687     // private
5688     showNextYear : function(){
5689         this.update(this.activeDate.add("y", 1));
5690     },
5691
5692     // private
5693     handleMouseWheel : function(e){
5694         var delta = e.getWheelDelta();
5695         if(delta > 0){
5696             this.showPrevMonth();
5697             e.stopEvent();
5698         } else if(delta < 0){
5699             this.showNextMonth();
5700             e.stopEvent();
5701         }
5702     },
5703
5704     // private
5705     handleDateClick : function(e, t){
5706         e.stopEvent();
5707         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
5708             this.setValue(new Date(t.dateValue));
5709             this.fireEvent("select", this, this.value);
5710         }
5711     },
5712
5713     // private
5714     selectToday : function(){
5715         this.setValue(new Date().clearTime());
5716         this.fireEvent("select", this, this.value);
5717     },
5718
5719     // private
5720     update : function(date)
5721     {
5722         var vd = this.activeDate;
5723         this.activeDate = date;
5724         if(vd && this.el){
5725             var t = date.getTime();
5726             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
5727                 this.cells.removeClass("x-date-selected");
5728                 this.cells.each(function(c){
5729                    if(c.dom.firstChild.dateValue == t){
5730                        c.addClass("x-date-selected");
5731                        setTimeout(function(){
5732                             try{c.dom.firstChild.focus();}catch(e){}
5733                        }, 50);
5734                        return false;
5735                    }
5736                 });
5737                 return;
5738             }
5739         }
5740         
5741         var days = date.getDaysInMonth();
5742         var firstOfMonth = date.getFirstDateOfMonth();
5743         var startingPos = firstOfMonth.getDay()-this.startDay;
5744
5745         if(startingPos <= this.startDay){
5746             startingPos += 7;
5747         }
5748
5749         var pm = date.add("mo", -1);
5750         var prevStart = pm.getDaysInMonth()-startingPos;
5751
5752         var cells = this.cells.elements;
5753         var textEls = this.textNodes;
5754         days += startingPos;
5755
5756         // convert everything to numbers so it's fast
5757         var day = 86400000;
5758         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
5759         var today = new Date().clearTime().getTime();
5760         var sel = date.clearTime().getTime();
5761         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
5762         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
5763         var ddMatch = this.disabledDatesRE;
5764         var ddText = this.disabledDatesText;
5765         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
5766         var ddaysText = this.disabledDaysText;
5767         var format = this.format;
5768
5769         var setCellClass = function(cal, cell){
5770             cell.title = "";
5771             var t = d.getTime();
5772             cell.firstChild.dateValue = t;
5773             if(t == today){
5774                 cell.className += " x-date-today";
5775                 cell.title = cal.todayText;
5776             }
5777             if(t == sel){
5778                 cell.className += " x-date-selected";
5779                 setTimeout(function(){
5780                     try{cell.firstChild.focus();}catch(e){}
5781                 }, 50);
5782             }
5783             // disabling
5784             if(t < min) {
5785                 cell.className = " x-date-disabled";
5786                 cell.title = cal.minText;
5787                 return;
5788             }
5789             if(t > max) {
5790                 cell.className = " x-date-disabled";
5791                 cell.title = cal.maxText;
5792                 return;
5793             }
5794             if(ddays){
5795                 if(ddays.indexOf(d.getDay()) != -1){
5796                     cell.title = ddaysText;
5797                     cell.className = " x-date-disabled";
5798                 }
5799             }
5800             if(ddMatch && format){
5801                 var fvalue = d.dateFormat(format);
5802                 if(ddMatch.test(fvalue)){
5803                     cell.title = ddText.replace("%0", fvalue);
5804                     cell.className = " x-date-disabled";
5805                 }
5806             }
5807         };
5808
5809         var i = 0;
5810         for(; i < startingPos; i++) {
5811             textEls[i].innerHTML = (++prevStart);
5812             d.setDate(d.getDate()+1);
5813             cells[i].className = "x-date-prevday";
5814             setCellClass(this, cells[i]);
5815         }
5816         for(; i < days; i++){
5817             intDay = i - startingPos + 1;
5818             textEls[i].innerHTML = (intDay);
5819             d.setDate(d.getDate()+1);
5820             cells[i].className = "x-date-active";
5821             setCellClass(this, cells[i]);
5822         }
5823         var extraDays = 0;
5824         for(; i < 42; i++) {
5825              textEls[i].innerHTML = (++extraDays);
5826              d.setDate(d.getDate()+1);
5827              cells[i].className = "x-date-nextday";
5828              setCellClass(this, cells[i]);
5829         }
5830
5831         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
5832         this.fireEvent('monthchange', this, date);
5833         
5834         if(!this.internalRender){
5835             var main = this.el.dom.firstChild;
5836             var w = main.offsetWidth;
5837             this.el.setWidth(w + this.el.getBorderWidth("lr"));
5838             Roo.fly(main).setWidth(w);
5839             this.internalRender = true;
5840             // opera does not respect the auto grow header center column
5841             // then, after it gets a width opera refuses to recalculate
5842             // without a second pass
5843             if(Roo.isOpera && !this.secondPass){
5844                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
5845                 this.secondPass = true;
5846                 this.update.defer(10, this, [date]);
5847             }
5848         }
5849         
5850         
5851     }
5852 });        /*
5853  * Based on:
5854  * Ext JS Library 1.1.1
5855  * Copyright(c) 2006-2007, Ext JS, LLC.
5856  *
5857  * Originally Released Under LGPL - original licence link has changed is not relivant.
5858  *
5859  * Fork - LGPL
5860  * <script type="text/javascript">
5861  */
5862 /**
5863  * @class Roo.TabPanel
5864  * @extends Roo.util.Observable
5865  * A lightweight tab container.
5866  * <br><br>
5867  * Usage:
5868  * <pre><code>
5869 // basic tabs 1, built from existing content
5870 var tabs = new Roo.TabPanel("tabs1");
5871 tabs.addTab("script", "View Script");
5872 tabs.addTab("markup", "View Markup");
5873 tabs.activate("script");
5874
5875 // more advanced tabs, built from javascript
5876 var jtabs = new Roo.TabPanel("jtabs");
5877 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
5878
5879 // set up the UpdateManager
5880 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
5881 var updater = tab2.getUpdateManager();
5882 updater.setDefaultUrl("ajax1.htm");
5883 tab2.on('activate', updater.refresh, updater, true);
5884
5885 // Use setUrl for Ajax loading
5886 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
5887 tab3.setUrl("ajax2.htm", null, true);
5888
5889 // Disabled tab
5890 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
5891 tab4.disable();
5892
5893 jtabs.activate("jtabs-1");
5894  * </code></pre>
5895  * @constructor
5896  * Create a new TabPanel.
5897  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
5898  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
5899  */
5900 Roo.TabPanel = function(container, config){
5901     /**
5902     * The container element for this TabPanel.
5903     * @type Roo.Element
5904     */
5905     this.el = Roo.get(container, true);
5906     if(config){
5907         if(typeof config == "boolean"){
5908             this.tabPosition = config ? "bottom" : "top";
5909         }else{
5910             Roo.apply(this, config);
5911         }
5912     }
5913     if(this.tabPosition == "bottom"){
5914         this.bodyEl = Roo.get(this.createBody(this.el.dom));
5915         this.el.addClass("x-tabs-bottom");
5916     }
5917     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
5918     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
5919     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
5920     if(Roo.isIE){
5921         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
5922     }
5923     if(this.tabPosition != "bottom"){
5924         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
5925          * @type Roo.Element
5926          */
5927         this.bodyEl = Roo.get(this.createBody(this.el.dom));
5928         this.el.addClass("x-tabs-top");
5929     }
5930     this.items = [];
5931
5932     this.bodyEl.setStyle("position", "relative");
5933
5934     this.active = null;
5935     this.activateDelegate = this.activate.createDelegate(this);
5936
5937     this.addEvents({
5938         /**
5939          * @event tabchange
5940          * Fires when the active tab changes
5941          * @param {Roo.TabPanel} this
5942          * @param {Roo.TabPanelItem} activePanel The new active tab
5943          */
5944         "tabchange": true,
5945         /**
5946          * @event beforetabchange
5947          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
5948          * @param {Roo.TabPanel} this
5949          * @param {Object} e Set cancel to true on this object to cancel the tab change
5950          * @param {Roo.TabPanelItem} tab The tab being changed to
5951          */
5952         "beforetabchange" : true
5953     });
5954
5955     Roo.EventManager.onWindowResize(this.onResize, this);
5956     this.cpad = this.el.getPadding("lr");
5957     this.hiddenCount = 0;
5958
5959
5960     // toolbar on the tabbar support...
5961     if (this.toolbar) {
5962         var tcfg = this.toolbar;
5963         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
5964         this.toolbar = new Roo.Toolbar(tcfg);
5965         if (Roo.isSafari) {
5966             var tbl = tcfg.container.child('table', true);
5967             tbl.setAttribute('width', '100%');
5968         }
5969         
5970     }
5971    
5972
5973
5974     Roo.TabPanel.superclass.constructor.call(this);
5975 };
5976
5977 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
5978     /*
5979      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
5980      */
5981     tabPosition : "top",
5982     /*
5983      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
5984      */
5985     currentTabWidth : 0,
5986     /*
5987      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
5988      */
5989     minTabWidth : 40,
5990     /*
5991      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
5992      */
5993     maxTabWidth : 250,
5994     /*
5995      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
5996      */
5997     preferredTabWidth : 175,
5998     /*
5999      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
6000      */
6001     resizeTabs : false,
6002     /*
6003      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
6004      */
6005     monitorResize : true,
6006     /*
6007      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
6008      */
6009     toolbar : false,
6010
6011     /**
6012      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
6013      * @param {String} id The id of the div to use <b>or create</b>
6014      * @param {String} text The text for the tab
6015      * @param {String} content (optional) Content to put in the TabPanelItem body
6016      * @param {Boolean} closable (optional) True to create a close icon on the tab
6017      * @return {Roo.TabPanelItem} The created TabPanelItem
6018      */
6019     addTab : function(id, text, content, closable){
6020         var item = new Roo.TabPanelItem(this, id, text, closable);
6021         this.addTabItem(item);
6022         if(content){
6023             item.setContent(content);
6024         }
6025         return item;
6026     },
6027
6028     /**
6029      * Returns the {@link Roo.TabPanelItem} with the specified id/index
6030      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
6031      * @return {Roo.TabPanelItem}
6032      */
6033     getTab : function(id){
6034         return this.items[id];
6035     },
6036
6037     /**
6038      * Hides the {@link Roo.TabPanelItem} with the specified id/index
6039      * @param {String/Number} id The id or index of the TabPanelItem to hide.
6040      */
6041     hideTab : function(id){
6042         var t = this.items[id];
6043         if(!t.isHidden()){
6044            t.setHidden(true);
6045            this.hiddenCount++;
6046            this.autoSizeTabs();
6047         }
6048     },
6049
6050     /**
6051      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
6052      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
6053      */
6054     unhideTab : function(id){
6055         var t = this.items[id];
6056         if(t.isHidden()){
6057            t.setHidden(false);
6058            this.hiddenCount--;
6059            this.autoSizeTabs();
6060         }
6061     },
6062
6063     /**
6064      * Adds an existing {@link Roo.TabPanelItem}.
6065      * @param {Roo.TabPanelItem} item The TabPanelItem to add
6066      */
6067     addTabItem : function(item){
6068         this.items[item.id] = item;
6069         this.items.push(item);
6070         if(this.resizeTabs){
6071            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
6072            this.autoSizeTabs();
6073         }else{
6074             item.autoSize();
6075         }
6076     },
6077
6078     /**
6079      * Removes a {@link Roo.TabPanelItem}.
6080      * @param {String/Number} id The id or index of the TabPanelItem to remove.
6081      */
6082     removeTab : function(id){
6083         var items = this.items;
6084         var tab = items[id];
6085         if(!tab) { return; }
6086         var index = items.indexOf(tab);
6087         if(this.active == tab && items.length > 1){
6088             var newTab = this.getNextAvailable(index);
6089             if(newTab) {
6090                 newTab.activate();
6091             }
6092         }
6093         this.stripEl.dom.removeChild(tab.pnode.dom);
6094         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
6095             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
6096         }
6097         items.splice(index, 1);
6098         delete this.items[tab.id];
6099         tab.fireEvent("close", tab);
6100         tab.purgeListeners();
6101         this.autoSizeTabs();
6102     },
6103
6104     getNextAvailable : function(start){
6105         var items = this.items;
6106         var index = start;
6107         // look for a next tab that will slide over to
6108         // replace the one being removed
6109         while(index < items.length){
6110             var item = items[++index];
6111             if(item && !item.isHidden()){
6112                 return item;
6113             }
6114         }
6115         // if one isn't found select the previous tab (on the left)
6116         index = start;
6117         while(index >= 0){
6118             var item = items[--index];
6119             if(item && !item.isHidden()){
6120                 return item;
6121             }
6122         }
6123         return null;
6124     },
6125
6126     /**
6127      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
6128      * @param {String/Number} id The id or index of the TabPanelItem to disable.
6129      */
6130     disableTab : function(id){
6131         var tab = this.items[id];
6132         if(tab && this.active != tab){
6133             tab.disable();
6134         }
6135     },
6136
6137     /**
6138      * Enables a {@link Roo.TabPanelItem} that is disabled.
6139      * @param {String/Number} id The id or index of the TabPanelItem to enable.
6140      */
6141     enableTab : function(id){
6142         var tab = this.items[id];
6143         tab.enable();
6144     },
6145
6146     /**
6147      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
6148      * @param {String/Number} id The id or index of the TabPanelItem to activate.
6149      * @return {Roo.TabPanelItem} The TabPanelItem.
6150      */
6151     activate : function(id){
6152         var tab = this.items[id];
6153         if(!tab){
6154             return null;
6155         }
6156         if(tab == this.active || tab.disabled){
6157             return tab;
6158         }
6159         var e = {};
6160         this.fireEvent("beforetabchange", this, e, tab);
6161         if(e.cancel !== true && !tab.disabled){
6162             if(this.active){
6163                 this.active.hide();
6164             }
6165             this.active = this.items[id];
6166             this.active.show();
6167             this.fireEvent("tabchange", this, this.active);
6168         }
6169         return tab;
6170     },
6171
6172     /**
6173      * Gets the active {@link Roo.TabPanelItem}.
6174      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
6175      */
6176     getActiveTab : function(){
6177         return this.active;
6178     },
6179
6180     /**
6181      * Updates the tab body element to fit the height of the container element
6182      * for overflow scrolling
6183      * @param {Number} targetHeight (optional) Override the starting height from the elements height
6184      */
6185     syncHeight : function(targetHeight){
6186         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
6187         var bm = this.bodyEl.getMargins();
6188         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
6189         this.bodyEl.setHeight(newHeight);
6190         return newHeight;
6191     },
6192
6193     onResize : function(){
6194         if(this.monitorResize){
6195             this.autoSizeTabs();
6196         }
6197     },
6198
6199     /**
6200      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
6201      */
6202     beginUpdate : function(){
6203         this.updating = true;
6204     },
6205
6206     /**
6207      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
6208      */
6209     endUpdate : function(){
6210         this.updating = false;
6211         this.autoSizeTabs();
6212     },
6213
6214     /**
6215      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
6216      */
6217     autoSizeTabs : function(){
6218         var count = this.items.length;
6219         var vcount = count - this.hiddenCount;
6220         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
6221             return;
6222         }
6223         var w = Math.max(this.el.getWidth() - this.cpad, 10);
6224         var availWidth = Math.floor(w / vcount);
6225         var b = this.stripBody;
6226         if(b.getWidth() > w){
6227             var tabs = this.items;
6228             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
6229             if(availWidth < this.minTabWidth){
6230                 /*if(!this.sleft){    // incomplete scrolling code
6231                     this.createScrollButtons();
6232                 }
6233                 this.showScroll();
6234                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
6235             }
6236         }else{
6237             if(this.currentTabWidth < this.preferredTabWidth){
6238                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
6239             }
6240         }
6241     },
6242
6243     /**
6244      * Returns the number of tabs in this TabPanel.
6245      * @return {Number}
6246      */
6247      getCount : function(){
6248          return this.items.length;
6249      },
6250
6251     /**
6252      * Resizes all the tabs to the passed width
6253      * @param {Number} The new width
6254      */
6255     setTabWidth : function(width){
6256         this.currentTabWidth = width;
6257         for(var i = 0, len = this.items.length; i < len; i++) {
6258                 if(!this.items[i].isHidden()) {
6259                 this.items[i].setWidth(width);
6260             }
6261         }
6262     },
6263
6264     /**
6265      * Destroys this TabPanel
6266      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
6267      */
6268     destroy : function(removeEl){
6269         Roo.EventManager.removeResizeListener(this.onResize, this);
6270         for(var i = 0, len = this.items.length; i < len; i++){
6271             this.items[i].purgeListeners();
6272         }
6273         if(removeEl === true){
6274             this.el.update("");
6275             this.el.remove();
6276         }
6277     }
6278 });
6279
6280 /**
6281  * @class Roo.TabPanelItem
6282  * @extends Roo.util.Observable
6283  * Represents an individual item (tab plus body) in a TabPanel.
6284  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
6285  * @param {String} id The id of this TabPanelItem
6286  * @param {String} text The text for the tab of this TabPanelItem
6287  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
6288  */
6289 Roo.TabPanelItem = function(tabPanel, id, text, closable){
6290     /**
6291      * The {@link Roo.TabPanel} this TabPanelItem belongs to
6292      * @type Roo.TabPanel
6293      */
6294     this.tabPanel = tabPanel;
6295     /**
6296      * The id for this TabPanelItem
6297      * @type String
6298      */
6299     this.id = id;
6300     /** @private */
6301     this.disabled = false;
6302     /** @private */
6303     this.text = text;
6304     /** @private */
6305     this.loaded = false;
6306     this.closable = closable;
6307
6308     /**
6309      * The body element for this TabPanelItem.
6310      * @type Roo.Element
6311      */
6312     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
6313     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
6314     this.bodyEl.setStyle("display", "block");
6315     this.bodyEl.setStyle("zoom", "1");
6316     this.hideAction();
6317
6318     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
6319     /** @private */
6320     this.el = Roo.get(els.el, true);
6321     this.inner = Roo.get(els.inner, true);
6322     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
6323     this.pnode = Roo.get(els.el.parentNode, true);
6324     this.el.on("mousedown", this.onTabMouseDown, this);
6325     this.el.on("click", this.onTabClick, this);
6326     /** @private */
6327     if(closable){
6328         var c = Roo.get(els.close, true);
6329         c.dom.title = this.closeText;
6330         c.addClassOnOver("close-over");
6331         c.on("click", this.closeClick, this);
6332      }
6333
6334     this.addEvents({
6335          /**
6336          * @event activate
6337          * Fires when this tab becomes the active tab.
6338          * @param {Roo.TabPanel} tabPanel The parent TabPanel
6339          * @param {Roo.TabPanelItem} this
6340          */
6341         "activate": true,
6342         /**
6343          * @event beforeclose
6344          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
6345          * @param {Roo.TabPanelItem} this
6346          * @param {Object} e Set cancel to true on this object to cancel the close.
6347          */
6348         "beforeclose": true,
6349         /**
6350          * @event close
6351          * Fires when this tab is closed.
6352          * @param {Roo.TabPanelItem} this
6353          */
6354          "close": true,
6355         /**
6356          * @event deactivate
6357          * Fires when this tab is no longer the active tab.
6358          * @param {Roo.TabPanel} tabPanel The parent TabPanel
6359          * @param {Roo.TabPanelItem} this
6360          */
6361          "deactivate" : true
6362     });
6363     this.hidden = false;
6364
6365     Roo.TabPanelItem.superclass.constructor.call(this);
6366 };
6367
6368 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
6369     purgeListeners : function(){
6370        Roo.util.Observable.prototype.purgeListeners.call(this);
6371        this.el.removeAllListeners();
6372     },
6373     /**
6374      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
6375      */
6376     show : function(){
6377         this.pnode.addClass("on");
6378         this.showAction();
6379         if(Roo.isOpera){
6380             this.tabPanel.stripWrap.repaint();
6381         }
6382         this.fireEvent("activate", this.tabPanel, this);
6383     },
6384
6385     /**
6386      * Returns true if this tab is the active tab.
6387      * @return {Boolean}
6388      */
6389     isActive : function(){
6390         return this.tabPanel.getActiveTab() == this;
6391     },
6392
6393     /**
6394      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
6395      */
6396     hide : function(){
6397         this.pnode.removeClass("on");
6398         this.hideAction();
6399         this.fireEvent("deactivate", this.tabPanel, this);
6400     },
6401
6402     hideAction : function(){
6403         this.bodyEl.hide();
6404         this.bodyEl.setStyle("position", "absolute");
6405         this.bodyEl.setLeft("-20000px");
6406         this.bodyEl.setTop("-20000px");
6407     },
6408
6409     showAction : function(){
6410         this.bodyEl.setStyle("position", "relative");
6411         this.bodyEl.setTop("");
6412         this.bodyEl.setLeft("");
6413         this.bodyEl.show();
6414     },
6415
6416     /**
6417      * Set the tooltip for the tab.
6418      * @param {String} tooltip The tab's tooltip
6419      */
6420     setTooltip : function(text){
6421         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
6422             this.textEl.dom.qtip = text;
6423             this.textEl.dom.removeAttribute('title');
6424         }else{
6425             this.textEl.dom.title = text;
6426         }
6427     },
6428
6429     onTabClick : function(e){
6430         e.preventDefault();
6431         this.tabPanel.activate(this.id);
6432     },
6433
6434     onTabMouseDown : function(e){
6435         e.preventDefault();
6436         this.tabPanel.activate(this.id);
6437     },
6438
6439     getWidth : function(){
6440         return this.inner.getWidth();
6441     },
6442
6443     setWidth : function(width){
6444         var iwidth = width - this.pnode.getPadding("lr");
6445         this.inner.setWidth(iwidth);
6446         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
6447         this.pnode.setWidth(width);
6448     },
6449
6450     /**
6451      * Show or hide the tab
6452      * @param {Boolean} hidden True to hide or false to show.
6453      */
6454     setHidden : function(hidden){
6455         this.hidden = hidden;
6456         this.pnode.setStyle("display", hidden ? "none" : "");
6457     },
6458
6459     /**
6460      * Returns true if this tab is "hidden"
6461      * @return {Boolean}
6462      */
6463     isHidden : function(){
6464         return this.hidden;
6465     },
6466
6467     /**
6468      * Returns the text for this tab
6469      * @return {String}
6470      */
6471     getText : function(){
6472         return this.text;
6473     },
6474
6475     autoSize : function(){
6476         //this.el.beginMeasure();
6477         this.textEl.setWidth(1);
6478         /*
6479          *  #2804 [new] Tabs in Roojs
6480          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
6481          */
6482         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
6483         //this.el.endMeasure();
6484     },
6485
6486     /**
6487      * Sets the text for the tab (Note: this also sets the tooltip text)
6488      * @param {String} text The tab's text and tooltip
6489      */
6490     setText : function(text){
6491         this.text = text;
6492         this.textEl.update(text);
6493         this.setTooltip(text);
6494         if(!this.tabPanel.resizeTabs){
6495             this.autoSize();
6496         }
6497     },
6498     /**
6499      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
6500      */
6501     activate : function(){
6502         this.tabPanel.activate(this.id);
6503     },
6504
6505     /**
6506      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
6507      */
6508     disable : function(){
6509         if(this.tabPanel.active != this){
6510             this.disabled = true;
6511             this.pnode.addClass("disabled");
6512         }
6513     },
6514
6515     /**
6516      * Enables this TabPanelItem if it was previously disabled.
6517      */
6518     enable : function(){
6519         this.disabled = false;
6520         this.pnode.removeClass("disabled");
6521     },
6522
6523     /**
6524      * Sets the content for this TabPanelItem.
6525      * @param {String} content The content
6526      * @param {Boolean} loadScripts true to look for and load scripts
6527      */
6528     setContent : function(content, loadScripts){
6529         this.bodyEl.update(content, loadScripts);
6530     },
6531
6532     /**
6533      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
6534      * @return {Roo.UpdateManager} The UpdateManager
6535      */
6536     getUpdateManager : function(){
6537         return this.bodyEl.getUpdateManager();
6538     },
6539
6540     /**
6541      * Set a URL to be used to load the content for this TabPanelItem.
6542      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
6543      * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
6544      * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this TabPanelItem is activated. (Defaults to false)
6545      * @return {Roo.UpdateManager} The UpdateManager
6546      */
6547     setUrl : function(url, params, loadOnce){
6548         if(this.refreshDelegate){
6549             this.un('activate', this.refreshDelegate);
6550         }
6551         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
6552         this.on("activate", this.refreshDelegate);
6553         return this.bodyEl.getUpdateManager();
6554     },
6555
6556     /** @private */
6557     _handleRefresh : function(url, params, loadOnce){
6558         if(!loadOnce || !this.loaded){
6559             var updater = this.bodyEl.getUpdateManager();
6560             updater.update(url, params, this._setLoaded.createDelegate(this));
6561         }
6562     },
6563
6564     /**
6565      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
6566      *   Will fail silently if the setUrl method has not been called.
6567      *   This does not activate the panel, just updates its content.
6568      */
6569     refresh : function(){
6570         if(this.refreshDelegate){
6571            this.loaded = false;
6572            this.refreshDelegate();
6573         }
6574     },
6575
6576     /** @private */
6577     _setLoaded : function(){
6578         this.loaded = true;
6579     },
6580
6581     /** @private */
6582     closeClick : function(e){
6583         var o = {};
6584         e.stopEvent();
6585         this.fireEvent("beforeclose", this, o);
6586         if(o.cancel !== true){
6587             this.tabPanel.removeTab(this.id);
6588         }
6589     },
6590     /**
6591      * The text displayed in the tooltip for the close icon.
6592      * @type String
6593      */
6594     closeText : "Close this tab"
6595 });
6596
6597 /** @private */
6598 Roo.TabPanel.prototype.createStrip = function(container){
6599     var strip = document.createElement("div");
6600     strip.className = "x-tabs-wrap";
6601     container.appendChild(strip);
6602     return strip;
6603 };
6604 /** @private */
6605 Roo.TabPanel.prototype.createStripList = function(strip){
6606     // div wrapper for retard IE
6607     // returns the "tr" element.
6608     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
6609         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
6610         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
6611     return strip.firstChild.firstChild.firstChild.firstChild;
6612 };
6613 /** @private */
6614 Roo.TabPanel.prototype.createBody = function(container){
6615     var body = document.createElement("div");
6616     Roo.id(body, "tab-body");
6617     Roo.fly(body).addClass("x-tabs-body");
6618     container.appendChild(body);
6619     return body;
6620 };
6621 /** @private */
6622 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
6623     var body = Roo.getDom(id);
6624     if(!body){
6625         body = document.createElement("div");
6626         body.id = id;
6627     }
6628     Roo.fly(body).addClass("x-tabs-item-body");
6629     bodyEl.insertBefore(body, bodyEl.firstChild);
6630     return body;
6631 };
6632 /** @private */
6633 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
6634     var td = document.createElement("td");
6635     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
6636     //stripEl.appendChild(td);
6637     if(closable){
6638         td.className = "x-tabs-closable";
6639         if(!this.closeTpl){
6640             this.closeTpl = new Roo.Template(
6641                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6642                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
6643                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
6644             );
6645         }
6646         var el = this.closeTpl.overwrite(td, {"text": text});
6647         var close = el.getElementsByTagName("div")[0];
6648         var inner = el.getElementsByTagName("em")[0];
6649         return {"el": el, "close": close, "inner": inner};
6650     } else {
6651         if(!this.tabTpl){
6652             this.tabTpl = new Roo.Template(
6653                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6654                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
6655             );
6656         }
6657         var el = this.tabTpl.overwrite(td, {"text": text});
6658         var inner = el.getElementsByTagName("em")[0];
6659         return {"el": el, "inner": inner};
6660     }
6661 };/*
6662  * Based on:
6663  * Ext JS Library 1.1.1
6664  * Copyright(c) 2006-2007, Ext JS, LLC.
6665  *
6666  * Originally Released Under LGPL - original licence link has changed is not relivant.
6667  *
6668  * Fork - LGPL
6669  * <script type="text/javascript">
6670  */
6671
6672 /**
6673  * @class Roo.Button
6674  * @extends Roo.util.Observable
6675  * Simple Button class
6676  * @cfg {String} text The button text
6677  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
6678  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
6679  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
6680  * @cfg {Object} scope The scope of the handler
6681  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
6682  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
6683  * @cfg {Boolean} hidden True to start hidden (defaults to false)
6684  * @cfg {Boolean} disabled True to start disabled (defaults to false)
6685  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
6686  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
6687    applies if enableToggle = true)
6688  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
6689  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
6690   an {@link Roo.util.ClickRepeater} config object (defaults to false).
6691  * @constructor
6692  * Create a new button
6693  * @param {Object} config The config object
6694  */
6695 Roo.Button = function(renderTo, config)
6696 {
6697     if (!config) {
6698         config = renderTo;
6699         renderTo = config.renderTo || false;
6700     }
6701     
6702     Roo.apply(this, config);
6703     this.addEvents({
6704         /**
6705              * @event click
6706              * Fires when this button is clicked
6707              * @param {Button} this
6708              * @param {EventObject} e The click event
6709              */
6710             "click" : true,
6711         /**
6712              * @event toggle
6713              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
6714              * @param {Button} this
6715              * @param {Boolean} pressed
6716              */
6717             "toggle" : true,
6718         /**
6719              * @event mouseover
6720              * Fires when the mouse hovers over the button
6721              * @param {Button} this
6722              * @param {Event} e The event object
6723              */
6724         'mouseover' : true,
6725         /**
6726              * @event mouseout
6727              * Fires when the mouse exits the button
6728              * @param {Button} this
6729              * @param {Event} e The event object
6730              */
6731         'mouseout': true,
6732          /**
6733              * @event render
6734              * Fires when the button is rendered
6735              * @param {Button} this
6736              */
6737         'render': true
6738     });
6739     if(this.menu){
6740         this.menu = Roo.menu.MenuMgr.get(this.menu);
6741     }
6742     // register listeners first!!  - so render can be captured..
6743     Roo.util.Observable.call(this);
6744     if(renderTo){
6745         this.render(renderTo);
6746     }
6747     
6748   
6749 };
6750
6751 Roo.extend(Roo.Button, Roo.util.Observable, {
6752     /**
6753      * 
6754      */
6755     
6756     /**
6757      * Read-only. True if this button is hidden
6758      * @type Boolean
6759      */
6760     hidden : false,
6761     /**
6762      * Read-only. True if this button is disabled
6763      * @type Boolean
6764      */
6765     disabled : false,
6766     /**
6767      * Read-only. True if this button is pressed (only if enableToggle = true)
6768      * @type Boolean
6769      */
6770     pressed : false,
6771
6772     /**
6773      * @cfg {Number} tabIndex 
6774      * The DOM tabIndex for this button (defaults to undefined)
6775      */
6776     tabIndex : undefined,
6777
6778     /**
6779      * @cfg {Boolean} enableToggle
6780      * True to enable pressed/not pressed toggling (defaults to false)
6781      */
6782     enableToggle: false,
6783     /**
6784      * @cfg {Mixed} menu
6785      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
6786      */
6787     menu : undefined,
6788     /**
6789      * @cfg {String} menuAlign
6790      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
6791      */
6792     menuAlign : "tl-bl?",
6793
6794     /**
6795      * @cfg {String} iconCls
6796      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
6797      */
6798     iconCls : undefined,
6799     /**
6800      * @cfg {String} type
6801      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
6802      */
6803     type : 'button',
6804
6805     // private
6806     menuClassTarget: 'tr',
6807
6808     /**
6809      * @cfg {String} clickEvent
6810      * The type of event to map to the button's event handler (defaults to 'click')
6811      */
6812     clickEvent : 'click',
6813
6814     /**
6815      * @cfg {Boolean} handleMouseEvents
6816      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
6817      */
6818     handleMouseEvents : true,
6819
6820     /**
6821      * @cfg {String} tooltipType
6822      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
6823      */
6824     tooltipType : 'qtip',
6825
6826     /**
6827      * @cfg {String} cls
6828      * A CSS class to apply to the button's main element.
6829      */
6830     
6831     /**
6832      * @cfg {Roo.Template} template (Optional)
6833      * An {@link Roo.Template} with which to create the Button's main element. This Template must
6834      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
6835      * require code modifications if required elements (e.g. a button) aren't present.
6836      */
6837
6838     // private
6839     render : function(renderTo){
6840         var btn;
6841         if(this.hideParent){
6842             this.parentEl = Roo.get(renderTo);
6843         }
6844         if(!this.dhconfig){
6845             if(!this.template){
6846                 if(!Roo.Button.buttonTemplate){
6847                     // hideous table template
6848                     Roo.Button.buttonTemplate = new Roo.Template(
6849                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
6850                         '<td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><em unselectable="on"><button class="x-btn-text" type="{1}">{0}</button></em></td><td class="x-btn-right"><i>&#160;</i></td>',
6851                         "</tr></tbody></table>");
6852                 }
6853                 this.template = Roo.Button.buttonTemplate;
6854             }
6855             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
6856             var btnEl = btn.child("button:first");
6857             btnEl.on('focus', this.onFocus, this);
6858             btnEl.on('blur', this.onBlur, this);
6859             if(this.cls){
6860                 btn.addClass(this.cls);
6861             }
6862             if(this.icon){
6863                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
6864             }
6865             if(this.iconCls){
6866                 btnEl.addClass(this.iconCls);
6867                 if(!this.cls){
6868                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
6869                 }
6870             }
6871             if(this.tabIndex !== undefined){
6872                 btnEl.dom.tabIndex = this.tabIndex;
6873             }
6874             if(this.tooltip){
6875                 if(typeof this.tooltip == 'object'){
6876                     Roo.QuickTips.tips(Roo.apply({
6877                           target: btnEl.id
6878                     }, this.tooltip));
6879                 } else {
6880                     btnEl.dom[this.tooltipType] = this.tooltip;
6881                 }
6882             }
6883         }else{
6884             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
6885         }
6886         this.el = btn;
6887         if(this.id){
6888             this.el.dom.id = this.el.id = this.id;
6889         }
6890         if(this.menu){
6891             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
6892             this.menu.on("show", this.onMenuShow, this);
6893             this.menu.on("hide", this.onMenuHide, this);
6894         }
6895         btn.addClass("x-btn");
6896         if(Roo.isIE && !Roo.isIE7){
6897             this.autoWidth.defer(1, this);
6898         }else{
6899             this.autoWidth();
6900         }
6901         if(this.handleMouseEvents){
6902             btn.on("mouseover", this.onMouseOver, this);
6903             btn.on("mouseout", this.onMouseOut, this);
6904             btn.on("mousedown", this.onMouseDown, this);
6905         }
6906         btn.on(this.clickEvent, this.onClick, this);
6907         //btn.on("mouseup", this.onMouseUp, this);
6908         if(this.hidden){
6909             this.hide();
6910         }
6911         if(this.disabled){
6912             this.disable();
6913         }
6914         Roo.ButtonToggleMgr.register(this);
6915         if(this.pressed){
6916             this.el.addClass("x-btn-pressed");
6917         }
6918         if(this.repeat){
6919             var repeater = new Roo.util.ClickRepeater(btn,
6920                 typeof this.repeat == "object" ? this.repeat : {}
6921             );
6922             repeater.on("click", this.onClick,  this);
6923         }
6924         
6925         this.fireEvent('render', this);
6926         
6927     },
6928     /**
6929      * Returns the button's underlying element
6930      * @return {Roo.Element} The element
6931      */
6932     getEl : function(){
6933         return this.el;  
6934     },
6935     
6936     /**
6937      * Destroys this Button and removes any listeners.
6938      */
6939     destroy : function(){
6940         Roo.ButtonToggleMgr.unregister(this);
6941         this.el.removeAllListeners();
6942         this.purgeListeners();
6943         this.el.remove();
6944     },
6945
6946     // private
6947     autoWidth : function(){
6948         if(this.el){
6949             this.el.setWidth("auto");
6950             if(Roo.isIE7 && Roo.isStrict){
6951                 var ib = this.el.child('button');
6952                 if(ib && ib.getWidth() > 20){
6953                     ib.clip();
6954                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
6955                 }
6956             }
6957             if(this.minWidth){
6958                 if(this.hidden){
6959                     this.el.beginMeasure();
6960                 }
6961                 if(this.el.getWidth() < this.minWidth){
6962                     this.el.setWidth(this.minWidth);
6963                 }
6964                 if(this.hidden){
6965                     this.el.endMeasure();
6966                 }
6967             }
6968         }
6969     },
6970
6971     /**
6972      * Assigns this button's click handler
6973      * @param {Function} handler The function to call when the button is clicked
6974      * @param {Object} scope (optional) Scope for the function passed in
6975      */
6976     setHandler : function(handler, scope){
6977         this.handler = handler;
6978         this.scope = scope;  
6979     },
6980     
6981     /**
6982      * Sets this button's text
6983      * @param {String} text The button text
6984      */
6985     setText : function(text){
6986         this.text = text;
6987         if(this.el){
6988             this.el.child("td.x-btn-center button.x-btn-text").update(text);
6989         }
6990         this.autoWidth();
6991     },
6992     
6993     /**
6994      * Gets the text for this button
6995      * @return {String} The button text
6996      */
6997     getText : function(){
6998         return this.text;  
6999     },
7000     
7001     /**
7002      * Show this button
7003      */
7004     show: function(){
7005         this.hidden = false;
7006         if(this.el){
7007             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
7008         }
7009     },
7010     
7011     /**
7012      * Hide this button
7013      */
7014     hide: function(){
7015         this.hidden = true;
7016         if(this.el){
7017             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
7018         }
7019     },
7020     
7021     /**
7022      * Convenience function for boolean show/hide
7023      * @param {Boolean} visible True to show, false to hide
7024      */
7025     setVisible: function(visible){
7026         if(visible) {
7027             this.show();
7028         }else{
7029             this.hide();
7030         }
7031     },
7032     
7033     /**
7034      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
7035      * @param {Boolean} state (optional) Force a particular state
7036      */
7037     toggle : function(state){
7038         state = state === undefined ? !this.pressed : state;
7039         if(state != this.pressed){
7040             if(state){
7041                 this.el.addClass("x-btn-pressed");
7042                 this.pressed = true;
7043                 this.fireEvent("toggle", this, true);
7044             }else{
7045                 this.el.removeClass("x-btn-pressed");
7046                 this.pressed = false;
7047                 this.fireEvent("toggle", this, false);
7048             }
7049             if(this.toggleHandler){
7050                 this.toggleHandler.call(this.scope || this, this, state);
7051             }
7052         }
7053     },
7054     
7055     /**
7056      * Focus the button
7057      */
7058     focus : function(){
7059         this.el.child('button:first').focus();
7060     },
7061     
7062     /**
7063      * Disable this button
7064      */
7065     disable : function(){
7066         if(this.el){
7067             this.el.addClass("x-btn-disabled");
7068         }
7069         this.disabled = true;
7070     },
7071     
7072     /**
7073      * Enable this button
7074      */
7075     enable : function(){
7076         if(this.el){
7077             this.el.removeClass("x-btn-disabled");
7078         }
7079         this.disabled = false;
7080     },
7081
7082     /**
7083      * Convenience function for boolean enable/disable
7084      * @param {Boolean} enabled True to enable, false to disable
7085      */
7086     setDisabled : function(v){
7087         this[v !== true ? "enable" : "disable"]();
7088     },
7089
7090     // private
7091     onClick : function(e)
7092     {
7093         if(e){
7094             e.preventDefault();
7095         }
7096         if(e.button != 0){
7097             return;
7098         }
7099         if(!this.disabled){
7100             if(this.enableToggle){
7101                 this.toggle();
7102             }
7103             if(this.menu && !this.menu.isVisible()){
7104                 this.menu.show(this.el, this.menuAlign);
7105             }
7106             this.fireEvent("click", this, e);
7107             if(this.handler){
7108                 this.el.removeClass("x-btn-over");
7109                 this.handler.call(this.scope || this, this, e);
7110             }
7111         }
7112     },
7113     // private
7114     onMouseOver : function(e){
7115         if(!this.disabled){
7116             this.el.addClass("x-btn-over");
7117             this.fireEvent('mouseover', this, e);
7118         }
7119     },
7120     // private
7121     onMouseOut : function(e){
7122         if(!e.within(this.el,  true)){
7123             this.el.removeClass("x-btn-over");
7124             this.fireEvent('mouseout', this, e);
7125         }
7126     },
7127     // private
7128     onFocus : function(e){
7129         if(!this.disabled){
7130             this.el.addClass("x-btn-focus");
7131         }
7132     },
7133     // private
7134     onBlur : function(e){
7135         this.el.removeClass("x-btn-focus");
7136     },
7137     // private
7138     onMouseDown : function(e){
7139         if(!this.disabled && e.button == 0){
7140             this.el.addClass("x-btn-click");
7141             Roo.get(document).on('mouseup', this.onMouseUp, this);
7142         }
7143     },
7144     // private
7145     onMouseUp : function(e){
7146         if(e.button == 0){
7147             this.el.removeClass("x-btn-click");
7148             Roo.get(document).un('mouseup', this.onMouseUp, this);
7149         }
7150     },
7151     // private
7152     onMenuShow : function(e){
7153         this.el.addClass("x-btn-menu-active");
7154     },
7155     // private
7156     onMenuHide : function(e){
7157         this.el.removeClass("x-btn-menu-active");
7158     }   
7159 });
7160
7161 // Private utility class used by Button
7162 Roo.ButtonToggleMgr = function(){
7163    var groups = {};
7164    
7165    function toggleGroup(btn, state){
7166        if(state){
7167            var g = groups[btn.toggleGroup];
7168            for(var i = 0, l = g.length; i < l; i++){
7169                if(g[i] != btn){
7170                    g[i].toggle(false);
7171                }
7172            }
7173        }
7174    }
7175    
7176    return {
7177        register : function(btn){
7178            if(!btn.toggleGroup){
7179                return;
7180            }
7181            var g = groups[btn.toggleGroup];
7182            if(!g){
7183                g = groups[btn.toggleGroup] = [];
7184            }
7185            g.push(btn);
7186            btn.on("toggle", toggleGroup);
7187        },
7188        
7189        unregister : function(btn){
7190            if(!btn.toggleGroup){
7191                return;
7192            }
7193            var g = groups[btn.toggleGroup];
7194            if(g){
7195                g.remove(btn);
7196                btn.un("toggle", toggleGroup);
7197            }
7198        }
7199    };
7200 }();/*
7201  * Based on:
7202  * Ext JS Library 1.1.1
7203  * Copyright(c) 2006-2007, Ext JS, LLC.
7204  *
7205  * Originally Released Under LGPL - original licence link has changed is not relivant.
7206  *
7207  * Fork - LGPL
7208  * <script type="text/javascript">
7209  */
7210  
7211 /**
7212  * @class Roo.SplitButton
7213  * @extends Roo.Button
7214  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
7215  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
7216  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
7217  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
7218  * @cfg {String} arrowTooltip The title attribute of the arrow
7219  * @constructor
7220  * Create a new menu button
7221  * @param {String/HTMLElement/Element} renderTo The element to append the button to
7222  * @param {Object} config The config object
7223  */
7224 Roo.SplitButton = function(renderTo, config){
7225     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
7226     /**
7227      * @event arrowclick
7228      * Fires when this button's arrow is clicked
7229      * @param {SplitButton} this
7230      * @param {EventObject} e The click event
7231      */
7232     this.addEvents({"arrowclick":true});
7233 };
7234
7235 Roo.extend(Roo.SplitButton, Roo.Button, {
7236     render : function(renderTo){
7237         // this is one sweet looking template!
7238         var tpl = new Roo.Template(
7239             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
7240             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
7241             '<tr><td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><button class="x-btn-text" type="{1}">{0}</button></td></tr>',
7242             "</tbody></table></td><td>",
7243             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
7244             '<tr><td class="x-btn-center"><button class="x-btn-menu-arrow-el" type="button">&#160;</button></td><td class="x-btn-right"><i>&#160;</i></td></tr>',
7245             "</tbody></table></td></tr></table>"
7246         );
7247         var btn = tpl.append(renderTo, [this.text, this.type], true);
7248         var btnEl = btn.child("button");
7249         if(this.cls){
7250             btn.addClass(this.cls);
7251         }
7252         if(this.icon){
7253             btnEl.setStyle('background-image', 'url(' +this.icon +')');
7254         }
7255         if(this.iconCls){
7256             btnEl.addClass(this.iconCls);
7257             if(!this.cls){
7258                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
7259             }
7260         }
7261         this.el = btn;
7262         if(this.handleMouseEvents){
7263             btn.on("mouseover", this.onMouseOver, this);
7264             btn.on("mouseout", this.onMouseOut, this);
7265             btn.on("mousedown", this.onMouseDown, this);
7266             btn.on("mouseup", this.onMouseUp, this);
7267         }
7268         btn.on(this.clickEvent, this.onClick, this);
7269         if(this.tooltip){
7270             if(typeof this.tooltip == 'object'){
7271                 Roo.QuickTips.tips(Roo.apply({
7272                       target: btnEl.id
7273                 }, this.tooltip));
7274             } else {
7275                 btnEl.dom[this.tooltipType] = this.tooltip;
7276             }
7277         }
7278         if(this.arrowTooltip){
7279             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
7280         }
7281         if(this.hidden){
7282             this.hide();
7283         }
7284         if(this.disabled){
7285             this.disable();
7286         }
7287         if(this.pressed){
7288             this.el.addClass("x-btn-pressed");
7289         }
7290         if(Roo.isIE && !Roo.isIE7){
7291             this.autoWidth.defer(1, this);
7292         }else{
7293             this.autoWidth();
7294         }
7295         if(this.menu){
7296             this.menu.on("show", this.onMenuShow, this);
7297             this.menu.on("hide", this.onMenuHide, this);
7298         }
7299         this.fireEvent('render', this);
7300     },
7301
7302     // private
7303     autoWidth : function(){
7304         if(this.el){
7305             var tbl = this.el.child("table:first");
7306             var tbl2 = this.el.child("table:last");
7307             this.el.setWidth("auto");
7308             tbl.setWidth("auto");
7309             if(Roo.isIE7 && Roo.isStrict){
7310                 var ib = this.el.child('button:first');
7311                 if(ib && ib.getWidth() > 20){
7312                     ib.clip();
7313                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
7314                 }
7315             }
7316             if(this.minWidth){
7317                 if(this.hidden){
7318                     this.el.beginMeasure();
7319                 }
7320                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
7321                     tbl.setWidth(this.minWidth-tbl2.getWidth());
7322                 }
7323                 if(this.hidden){
7324                     this.el.endMeasure();
7325                 }
7326             }
7327             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
7328         } 
7329     },
7330     /**
7331      * Sets this button's click handler
7332      * @param {Function} handler The function to call when the button is clicked
7333      * @param {Object} scope (optional) Scope for the function passed above
7334      */
7335     setHandler : function(handler, scope){
7336         this.handler = handler;
7337         this.scope = scope;  
7338     },
7339     
7340     /**
7341      * Sets this button's arrow click handler
7342      * @param {Function} handler The function to call when the arrow is clicked
7343      * @param {Object} scope (optional) Scope for the function passed above
7344      */
7345     setArrowHandler : function(handler, scope){
7346         this.arrowHandler = handler;
7347         this.scope = scope;  
7348     },
7349     
7350     /**
7351      * Focus the button
7352      */
7353     focus : function(){
7354         if(this.el){
7355             this.el.child("button:first").focus();
7356         }
7357     },
7358
7359     // private
7360     onClick : function(e){
7361         e.preventDefault();
7362         if(!this.disabled){
7363             if(e.getTarget(".x-btn-menu-arrow-wrap")){
7364                 if(this.menu && !this.menu.isVisible()){
7365                     this.menu.show(this.el, this.menuAlign);
7366                 }
7367                 this.fireEvent("arrowclick", this, e);
7368                 if(this.arrowHandler){
7369                     this.arrowHandler.call(this.scope || this, this, e);
7370                 }
7371             }else{
7372                 this.fireEvent("click", this, e);
7373                 if(this.handler){
7374                     this.handler.call(this.scope || this, this, e);
7375                 }
7376             }
7377         }
7378     },
7379     // private
7380     onMouseDown : function(e){
7381         if(!this.disabled){
7382             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
7383         }
7384     },
7385     // private
7386     onMouseUp : function(e){
7387         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
7388     }   
7389 });
7390
7391
7392 // backwards compat
7393 Roo.MenuButton = Roo.SplitButton;/*
7394  * Based on:
7395  * Ext JS Library 1.1.1
7396  * Copyright(c) 2006-2007, Ext JS, LLC.
7397  *
7398  * Originally Released Under LGPL - original licence link has changed is not relivant.
7399  *
7400  * Fork - LGPL
7401  * <script type="text/javascript">
7402  */
7403
7404 /**
7405  * @class Roo.Toolbar
7406  * Basic Toolbar class.
7407  * @constructor
7408  * Creates a new Toolbar
7409  * @param {Object} container The config object
7410  */ 
7411 Roo.Toolbar = function(container, buttons, config)
7412 {
7413     /// old consturctor format still supported..
7414     if(container instanceof Array){ // omit the container for later rendering
7415         buttons = container;
7416         config = buttons;
7417         container = null;
7418     }
7419     if (typeof(container) == 'object' && container.xtype) {
7420         config = container;
7421         container = config.container;
7422         buttons = config.buttons || []; // not really - use items!!
7423     }
7424     var xitems = [];
7425     if (config && config.items) {
7426         xitems = config.items;
7427         delete config.items;
7428     }
7429     Roo.apply(this, config);
7430     this.buttons = buttons;
7431     
7432     if(container){
7433         this.render(container);
7434     }
7435     this.xitems = xitems;
7436     Roo.each(xitems, function(b) {
7437         this.add(b);
7438     }, this);
7439     
7440 };
7441
7442 Roo.Toolbar.prototype = {
7443     /**
7444      * @cfg {Array} items
7445      * array of button configs or elements to add (will be converted to a MixedCollection)
7446      */
7447     
7448     /**
7449      * @cfg {String/HTMLElement/Element} container
7450      * The id or element that will contain the toolbar
7451      */
7452     // private
7453     render : function(ct){
7454         this.el = Roo.get(ct);
7455         if(this.cls){
7456             this.el.addClass(this.cls);
7457         }
7458         // using a table allows for vertical alignment
7459         // 100% width is needed by Safari...
7460         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
7461         this.tr = this.el.child("tr", true);
7462         var autoId = 0;
7463         this.items = new Roo.util.MixedCollection(false, function(o){
7464             return o.id || ("item" + (++autoId));
7465         });
7466         if(this.buttons){
7467             this.add.apply(this, this.buttons);
7468             delete this.buttons;
7469         }
7470     },
7471
7472     /**
7473      * Adds element(s) to the toolbar -- this function takes a variable number of 
7474      * arguments of mixed type and adds them to the toolbar.
7475      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
7476      * <ul>
7477      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
7478      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
7479      * <li>Field: Any form field (equivalent to {@link #addField})</li>
7480      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
7481      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
7482      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
7483      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
7484      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
7485      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
7486      * </ul>
7487      * @param {Mixed} arg2
7488      * @param {Mixed} etc.
7489      */
7490     add : function(){
7491         var a = arguments, l = a.length;
7492         for(var i = 0; i < l; i++){
7493             this._add(a[i]);
7494         }
7495     },
7496     // private..
7497     _add : function(el) {
7498         
7499         if (el.xtype) {
7500             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
7501         }
7502         
7503         if (el.applyTo){ // some kind of form field
7504             return this.addField(el);
7505         } 
7506         if (el.render){ // some kind of Toolbar.Item
7507             return this.addItem(el);
7508         }
7509         if (typeof el == "string"){ // string
7510             if(el == "separator" || el == "-"){
7511                 return this.addSeparator();
7512             }
7513             if (el == " "){
7514                 return this.addSpacer();
7515             }
7516             if(el == "->"){
7517                 return this.addFill();
7518             }
7519             return this.addText(el);
7520             
7521         }
7522         if(el.tagName){ // element
7523             return this.addElement(el);
7524         }
7525         if(typeof el == "object"){ // must be button config?
7526             return this.addButton(el);
7527         }
7528         // and now what?!?!
7529         return false;
7530         
7531     },
7532     
7533     /**
7534      * Add an Xtype element
7535      * @param {Object} xtype Xtype Object
7536      * @return {Object} created Object
7537      */
7538     addxtype : function(e){
7539         return this.add(e);  
7540     },
7541     
7542     /**
7543      * Returns the Element for this toolbar.
7544      * @return {Roo.Element}
7545      */
7546     getEl : function(){
7547         return this.el;  
7548     },
7549     
7550     /**
7551      * Adds a separator
7552      * @return {Roo.Toolbar.Item} The separator item
7553      */
7554     addSeparator : function(){
7555         return this.addItem(new Roo.Toolbar.Separator());
7556     },
7557
7558     /**
7559      * Adds a spacer element
7560      * @return {Roo.Toolbar.Spacer} The spacer item
7561      */
7562     addSpacer : function(){
7563         return this.addItem(new Roo.Toolbar.Spacer());
7564     },
7565
7566     /**
7567      * Adds a fill element that forces subsequent additions to the right side of the toolbar
7568      * @return {Roo.Toolbar.Fill} The fill item
7569      */
7570     addFill : function(){
7571         return this.addItem(new Roo.Toolbar.Fill());
7572     },
7573
7574     /**
7575      * Adds any standard HTML element to the toolbar
7576      * @param {String/HTMLElement/Element} el The element or id of the element to add
7577      * @return {Roo.Toolbar.Item} The element's item
7578      */
7579     addElement : function(el){
7580         return this.addItem(new Roo.Toolbar.Item(el));
7581     },
7582     /**
7583      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
7584      * @type Roo.util.MixedCollection  
7585      */
7586     items : false,
7587      
7588     /**
7589      * Adds any Toolbar.Item or subclass
7590      * @param {Roo.Toolbar.Item} item
7591      * @return {Roo.Toolbar.Item} The item
7592      */
7593     addItem : function(item){
7594         var td = this.nextBlock();
7595         item.render(td);
7596         this.items.add(item);
7597         return item;
7598     },
7599     
7600     /**
7601      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
7602      * @param {Object/Array} config A button config or array of configs
7603      * @return {Roo.Toolbar.Button/Array}
7604      */
7605     addButton : function(config){
7606         if(config instanceof Array){
7607             var buttons = [];
7608             for(var i = 0, len = config.length; i < len; i++) {
7609                 buttons.push(this.addButton(config[i]));
7610             }
7611             return buttons;
7612         }
7613         var b = config;
7614         if(!(config instanceof Roo.Toolbar.Button)){
7615             b = config.split ?
7616                 new Roo.Toolbar.SplitButton(config) :
7617                 new Roo.Toolbar.Button(config);
7618         }
7619         var td = this.nextBlock();
7620         b.render(td);
7621         this.items.add(b);
7622         return b;
7623     },
7624     
7625     /**
7626      * Adds text to the toolbar
7627      * @param {String} text The text to add
7628      * @return {Roo.Toolbar.Item} The element's item
7629      */
7630     addText : function(text){
7631         return this.addItem(new Roo.Toolbar.TextItem(text));
7632     },
7633     
7634     /**
7635      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
7636      * @param {Number} index The index where the item is to be inserted
7637      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
7638      * @return {Roo.Toolbar.Button/Item}
7639      */
7640     insertButton : function(index, item){
7641         if(item instanceof Array){
7642             var buttons = [];
7643             for(var i = 0, len = item.length; i < len; i++) {
7644                buttons.push(this.insertButton(index + i, item[i]));
7645             }
7646             return buttons;
7647         }
7648         if (!(item instanceof Roo.Toolbar.Button)){
7649            item = new Roo.Toolbar.Button(item);
7650         }
7651         var td = document.createElement("td");
7652         this.tr.insertBefore(td, this.tr.childNodes[index]);
7653         item.render(td);
7654         this.items.insert(index, item);
7655         return item;
7656     },
7657     
7658     /**
7659      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
7660      * @param {Object} config
7661      * @return {Roo.Toolbar.Item} The element's item
7662      */
7663     addDom : function(config, returnEl){
7664         var td = this.nextBlock();
7665         Roo.DomHelper.overwrite(td, config);
7666         var ti = new Roo.Toolbar.Item(td.firstChild);
7667         ti.render(td);
7668         this.items.add(ti);
7669         return ti;
7670     },
7671
7672     /**
7673      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
7674      * @type Roo.util.MixedCollection  
7675      */
7676     fields : false,
7677     
7678     /**
7679      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
7680      * Note: the field should not have been rendered yet. For a field that has already been
7681      * rendered, use {@link #addElement}.
7682      * @param {Roo.form.Field} field
7683      * @return {Roo.ToolbarItem}
7684      */
7685      
7686       
7687     addField : function(field) {
7688         if (!this.fields) {
7689             var autoId = 0;
7690             this.fields = new Roo.util.MixedCollection(false, function(o){
7691                 return o.id || ("item" + (++autoId));
7692             });
7693
7694         }
7695         
7696         var td = this.nextBlock();
7697         field.render(td);
7698         var ti = new Roo.Toolbar.Item(td.firstChild);
7699         ti.render(td);
7700         this.items.add(ti);
7701         this.fields.add(field);
7702         return ti;
7703     },
7704     /**
7705      * Hide the toolbar
7706      * @method hide
7707      */
7708      
7709       
7710     hide : function()
7711     {
7712         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
7713         this.el.child('div').hide();
7714     },
7715     /**
7716      * Show the toolbar
7717      * @method show
7718      */
7719     show : function()
7720     {
7721         this.el.child('div').show();
7722     },
7723       
7724     // private
7725     nextBlock : function(){
7726         var td = document.createElement("td");
7727         this.tr.appendChild(td);
7728         return td;
7729     },
7730
7731     // private
7732     destroy : function(){
7733         if(this.items){ // rendered?
7734             Roo.destroy.apply(Roo, this.items.items);
7735         }
7736         if(this.fields){ // rendered?
7737             Roo.destroy.apply(Roo, this.fields.items);
7738         }
7739         Roo.Element.uncache(this.el, this.tr);
7740     }
7741 };
7742
7743 /**
7744  * @class Roo.Toolbar.Item
7745  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
7746  * @constructor
7747  * Creates a new Item
7748  * @param {HTMLElement} el 
7749  */
7750 Roo.Toolbar.Item = function(el){
7751     var cfg = {};
7752     if (typeof (el.xtype) != 'undefined') {
7753         cfg = el;
7754         el = cfg.el;
7755     }
7756     
7757     this.el = Roo.getDom(el);
7758     this.id = Roo.id(this.el);
7759     this.hidden = false;
7760     
7761     this.addEvents({
7762          /**
7763              * @event render
7764              * Fires when the button is rendered
7765              * @param {Button} this
7766              */
7767         'render': true
7768     });
7769     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
7770 };
7771 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
7772 //Roo.Toolbar.Item.prototype = {
7773     
7774     /**
7775      * Get this item's HTML Element
7776      * @return {HTMLElement}
7777      */
7778     getEl : function(){
7779        return this.el;  
7780     },
7781
7782     // private
7783     render : function(td){
7784         
7785          this.td = td;
7786         td.appendChild(this.el);
7787         
7788         this.fireEvent('render', this);
7789     },
7790     
7791     /**
7792      * Removes and destroys this item.
7793      */
7794     destroy : function(){
7795         this.td.parentNode.removeChild(this.td);
7796     },
7797     
7798     /**
7799      * Shows this item.
7800      */
7801     show: function(){
7802         this.hidden = false;
7803         this.td.style.display = "";
7804     },
7805     
7806     /**
7807      * Hides this item.
7808      */
7809     hide: function(){
7810         this.hidden = true;
7811         this.td.style.display = "none";
7812     },
7813     
7814     /**
7815      * Convenience function for boolean show/hide.
7816      * @param {Boolean} visible true to show/false to hide
7817      */
7818     setVisible: function(visible){
7819         if(visible) {
7820             this.show();
7821         }else{
7822             this.hide();
7823         }
7824     },
7825     
7826     /**
7827      * Try to focus this item.
7828      */
7829     focus : function(){
7830         Roo.fly(this.el).focus();
7831     },
7832     
7833     /**
7834      * Disables this item.
7835      */
7836     disable : function(){
7837         Roo.fly(this.td).addClass("x-item-disabled");
7838         this.disabled = true;
7839         this.el.disabled = true;
7840     },
7841     
7842     /**
7843      * Enables this item.
7844      */
7845     enable : function(){
7846         Roo.fly(this.td).removeClass("x-item-disabled");
7847         this.disabled = false;
7848         this.el.disabled = false;
7849     }
7850 });
7851
7852
7853 /**
7854  * @class Roo.Toolbar.Separator
7855  * @extends Roo.Toolbar.Item
7856  * A simple toolbar separator class
7857  * @constructor
7858  * Creates a new Separator
7859  */
7860 Roo.Toolbar.Separator = function(cfg){
7861     
7862     var s = document.createElement("span");
7863     s.className = "ytb-sep";
7864     if (cfg) {
7865         cfg.el = s;
7866     }
7867     
7868     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
7869 };
7870 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
7871     enable:Roo.emptyFn,
7872     disable:Roo.emptyFn,
7873     focus:Roo.emptyFn
7874 });
7875
7876 /**
7877  * @class Roo.Toolbar.Spacer
7878  * @extends Roo.Toolbar.Item
7879  * A simple element that adds extra horizontal space to a toolbar.
7880  * @constructor
7881  * Creates a new Spacer
7882  */
7883 Roo.Toolbar.Spacer = function(cfg){
7884     var s = document.createElement("div");
7885     s.className = "ytb-spacer";
7886     if (cfg) {
7887         cfg.el = s;
7888     }
7889     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
7890 };
7891 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
7892     enable:Roo.emptyFn,
7893     disable:Roo.emptyFn,
7894     focus:Roo.emptyFn
7895 });
7896
7897 /**
7898  * @class Roo.Toolbar.Fill
7899  * @extends Roo.Toolbar.Spacer
7900  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
7901  * @constructor
7902  * Creates a new Spacer
7903  */
7904 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
7905     // private
7906     render : function(td){
7907         td.style.width = '100%';
7908         Roo.Toolbar.Fill.superclass.render.call(this, td);
7909     }
7910 });
7911
7912 /**
7913  * @class Roo.Toolbar.TextItem
7914  * @extends Roo.Toolbar.Item
7915  * A simple class that renders text directly into a toolbar.
7916  * @constructor
7917  * Creates a new TextItem
7918  * @param {String} text
7919  */
7920 Roo.Toolbar.TextItem = function(cfg){
7921     var  text = cfg || "";
7922     if (typeof(cfg) == 'object') {
7923         text = cfg.text || "";
7924     }  else {
7925         cfg = null;
7926     }
7927     var s = document.createElement("span");
7928     s.className = "ytb-text";
7929     s.innerHTML = text;
7930     if (cfg) {
7931         cfg.el  = s;
7932     }
7933     
7934     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
7935 };
7936 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
7937     
7938      
7939     enable:Roo.emptyFn,
7940     disable:Roo.emptyFn,
7941     focus:Roo.emptyFn
7942 });
7943
7944 /**
7945  * @class Roo.Toolbar.Button
7946  * @extends Roo.Button
7947  * A button that renders into a toolbar.
7948  * @constructor
7949  * Creates a new Button
7950  * @param {Object} config A standard {@link Roo.Button} config object
7951  */
7952 Roo.Toolbar.Button = function(config){
7953     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
7954 };
7955 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
7956     render : function(td){
7957         this.td = td;
7958         Roo.Toolbar.Button.superclass.render.call(this, td);
7959     },
7960     
7961     /**
7962      * Removes and destroys this button
7963      */
7964     destroy : function(){
7965         Roo.Toolbar.Button.superclass.destroy.call(this);
7966         this.td.parentNode.removeChild(this.td);
7967     },
7968     
7969     /**
7970      * Shows this button
7971      */
7972     show: function(){
7973         this.hidden = false;
7974         this.td.style.display = "";
7975     },
7976     
7977     /**
7978      * Hides this button
7979      */
7980     hide: function(){
7981         this.hidden = true;
7982         this.td.style.display = "none";
7983     },
7984
7985     /**
7986      * Disables this item
7987      */
7988     disable : function(){
7989         Roo.fly(this.td).addClass("x-item-disabled");
7990         this.disabled = true;
7991     },
7992
7993     /**
7994      * Enables this item
7995      */
7996     enable : function(){
7997         Roo.fly(this.td).removeClass("x-item-disabled");
7998         this.disabled = false;
7999     }
8000 });
8001 // backwards compat
8002 Roo.ToolbarButton = Roo.Toolbar.Button;
8003
8004 /**
8005  * @class Roo.Toolbar.SplitButton
8006  * @extends Roo.SplitButton
8007  * A menu button that renders into a toolbar.
8008  * @constructor
8009  * Creates a new SplitButton
8010  * @param {Object} config A standard {@link Roo.SplitButton} config object
8011  */
8012 Roo.Toolbar.SplitButton = function(config){
8013     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
8014 };
8015 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
8016     render : function(td){
8017         this.td = td;
8018         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
8019     },
8020     
8021     /**
8022      * Removes and destroys this button
8023      */
8024     destroy : function(){
8025         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
8026         this.td.parentNode.removeChild(this.td);
8027     },
8028     
8029     /**
8030      * Shows this button
8031      */
8032     show: function(){
8033         this.hidden = false;
8034         this.td.style.display = "";
8035     },
8036     
8037     /**
8038      * Hides this button
8039      */
8040     hide: function(){
8041         this.hidden = true;
8042         this.td.style.display = "none";
8043     }
8044 });
8045
8046 // backwards compat
8047 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
8048  * Based on:
8049  * Ext JS Library 1.1.1
8050  * Copyright(c) 2006-2007, Ext JS, LLC.
8051  *
8052  * Originally Released Under LGPL - original licence link has changed is not relivant.
8053  *
8054  * Fork - LGPL
8055  * <script type="text/javascript">
8056  */
8057  
8058 /**
8059  * @class Roo.PagingToolbar
8060  * @extends Roo.Toolbar
8061  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
8062  * @constructor
8063  * Create a new PagingToolbar
8064  * @param {Object} config The config object
8065  */
8066 Roo.PagingToolbar = function(el, ds, config)
8067 {
8068     // old args format still supported... - xtype is prefered..
8069     if (typeof(el) == 'object' && el.xtype) {
8070         // created from xtype...
8071         config = el;
8072         ds = el.dataSource;
8073         el = config.container;
8074     }
8075     var items = [];
8076     if (config.items) {
8077         items = config.items;
8078         config.items = [];
8079     }
8080     
8081     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
8082     this.ds = ds;
8083     this.cursor = 0;
8084     this.renderButtons(this.el);
8085     this.bind(ds);
8086     
8087     // supprot items array.
8088    
8089     Roo.each(items, function(e) {
8090         this.add(Roo.factory(e));
8091     },this);
8092     
8093 };
8094
8095 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
8096     /**
8097      * @cfg {Roo.data.Store} dataSource
8098      * The underlying data store providing the paged data
8099      */
8100     /**
8101      * @cfg {String/HTMLElement/Element} container
8102      * container The id or element that will contain the toolbar
8103      */
8104     /**
8105      * @cfg {Boolean} displayInfo
8106      * True to display the displayMsg (defaults to false)
8107      */
8108     /**
8109      * @cfg {Number} pageSize
8110      * The number of records to display per page (defaults to 20)
8111      */
8112     pageSize: 20,
8113     /**
8114      * @cfg {String} displayMsg
8115      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
8116      */
8117     displayMsg : 'Displaying {0} - {1} of {2}',
8118     /**
8119      * @cfg {String} emptyMsg
8120      * The message to display when no records are found (defaults to "No data to display")
8121      */
8122     emptyMsg : 'No data to display',
8123     /**
8124      * Customizable piece of the default paging text (defaults to "Page")
8125      * @type String
8126      */
8127     beforePageText : "Page",
8128     /**
8129      * Customizable piece of the default paging text (defaults to "of %0")
8130      * @type String
8131      */
8132     afterPageText : "of {0}",
8133     /**
8134      * Customizable piece of the default paging text (defaults to "First Page")
8135      * @type String
8136      */
8137     firstText : "First Page",
8138     /**
8139      * Customizable piece of the default paging text (defaults to "Previous Page")
8140      * @type String
8141      */
8142     prevText : "Previous Page",
8143     /**
8144      * Customizable piece of the default paging text (defaults to "Next Page")
8145      * @type String
8146      */
8147     nextText : "Next Page",
8148     /**
8149      * Customizable piece of the default paging text (defaults to "Last Page")
8150      * @type String
8151      */
8152     lastText : "Last Page",
8153     /**
8154      * Customizable piece of the default paging text (defaults to "Refresh")
8155      * @type String
8156      */
8157     refreshText : "Refresh",
8158
8159     // private
8160     renderButtons : function(el){
8161         Roo.PagingToolbar.superclass.render.call(this, el);
8162         this.first = this.addButton({
8163             tooltip: this.firstText,
8164             cls: "x-btn-icon x-grid-page-first",
8165             disabled: true,
8166             handler: this.onClick.createDelegate(this, ["first"])
8167         });
8168         this.prev = this.addButton({
8169             tooltip: this.prevText,
8170             cls: "x-btn-icon x-grid-page-prev",
8171             disabled: true,
8172             handler: this.onClick.createDelegate(this, ["prev"])
8173         });
8174         //this.addSeparator();
8175         this.add(this.beforePageText);
8176         this.field = Roo.get(this.addDom({
8177            tag: "input",
8178            type: "text",
8179            size: "3",
8180            value: "1",
8181            cls: "x-grid-page-number"
8182         }).el);
8183         this.field.on("keydown", this.onPagingKeydown, this);
8184         this.field.on("focus", function(){this.dom.select();});
8185         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
8186         this.field.setHeight(18);
8187         //this.addSeparator();
8188         this.next = this.addButton({
8189             tooltip: this.nextText,
8190             cls: "x-btn-icon x-grid-page-next",
8191             disabled: true,
8192             handler: this.onClick.createDelegate(this, ["next"])
8193         });
8194         this.last = this.addButton({
8195             tooltip: this.lastText,
8196             cls: "x-btn-icon x-grid-page-last",
8197             disabled: true,
8198             handler: this.onClick.createDelegate(this, ["last"])
8199         });
8200         //this.addSeparator();
8201         this.loading = this.addButton({
8202             tooltip: this.refreshText,
8203             cls: "x-btn-icon x-grid-loading",
8204             handler: this.onClick.createDelegate(this, ["refresh"])
8205         });
8206
8207         if(this.displayInfo){
8208             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
8209         }
8210     },
8211
8212     // private
8213     updateInfo : function(){
8214         if(this.displayEl){
8215             var count = this.ds.getCount();
8216             var msg = count == 0 ?
8217                 this.emptyMsg :
8218                 String.format(
8219                     this.displayMsg,
8220                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
8221                 );
8222             this.displayEl.update(msg);
8223         }
8224     },
8225
8226     // private
8227     onLoad : function(ds, r, o){
8228        this.cursor = o.params ? o.params.start : 0;
8229        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
8230
8231        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
8232        this.field.dom.value = ap;
8233        this.first.setDisabled(ap == 1);
8234        this.prev.setDisabled(ap == 1);
8235        this.next.setDisabled(ap == ps);
8236        this.last.setDisabled(ap == ps);
8237        this.loading.enable();
8238        this.updateInfo();
8239     },
8240
8241     // private
8242     getPageData : function(){
8243         var total = this.ds.getTotalCount();
8244         return {
8245             total : total,
8246             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
8247             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
8248         };
8249     },
8250
8251     // private
8252     onLoadError : function(){
8253         this.loading.enable();
8254     },
8255
8256     // private
8257     onPagingKeydown : function(e){
8258         var k = e.getKey();
8259         var d = this.getPageData();
8260         if(k == e.RETURN){
8261             var v = this.field.dom.value, pageNum;
8262             if(!v || isNaN(pageNum = parseInt(v, 10))){
8263                 this.field.dom.value = d.activePage;
8264                 return;
8265             }
8266             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
8267             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
8268             e.stopEvent();
8269         }
8270         else if(k == e.HOME || (k == e.UP && e.ctrlKey) || (k == e.PAGEUP && e.ctrlKey) || (k == e.RIGHT && e.ctrlKey) || k == e.END || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey))
8271         {
8272           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
8273           this.field.dom.value = pageNum;
8274           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
8275           e.stopEvent();
8276         }
8277         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
8278         {
8279           var v = this.field.dom.value, pageNum; 
8280           var increment = (e.shiftKey) ? 10 : 1;
8281           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
8282             increment *= -1;
8283           }
8284           if(!v || isNaN(pageNum = parseInt(v, 10))) {
8285             this.field.dom.value = d.activePage;
8286             return;
8287           }
8288           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
8289           {
8290             this.field.dom.value = parseInt(v, 10) + increment;
8291             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
8292             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
8293           }
8294           e.stopEvent();
8295         }
8296     },
8297
8298     // private
8299     beforeLoad : function(){
8300         if(this.loading){
8301             this.loading.disable();
8302         }
8303     },
8304
8305     // private
8306     onClick : function(which){
8307         var ds = this.ds;
8308         switch(which){
8309             case "first":
8310                 ds.load({params:{start: 0, limit: this.pageSize}});
8311             break;
8312             case "prev":
8313                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
8314             break;
8315             case "next":
8316                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
8317             break;
8318             case "last":
8319                 var total = ds.getTotalCount();
8320                 var extra = total % this.pageSize;
8321                 var lastStart = extra ? (total - extra) : total-this.pageSize;
8322                 ds.load({params:{start: lastStart, limit: this.pageSize}});
8323             break;
8324             case "refresh":
8325                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
8326             break;
8327         }
8328     },
8329
8330     /**
8331      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
8332      * @param {Roo.data.Store} store The data store to unbind
8333      */
8334     unbind : function(ds){
8335         ds.un("beforeload", this.beforeLoad, this);
8336         ds.un("load", this.onLoad, this);
8337         ds.un("loadexception", this.onLoadError, this);
8338         ds.un("remove", this.updateInfo, this);
8339         ds.un("add", this.updateInfo, this);
8340         this.ds = undefined;
8341     },
8342
8343     /**
8344      * Binds the paging toolbar to the specified {@link Roo.data.Store}
8345      * @param {Roo.data.Store} store The data store to bind
8346      */
8347     bind : function(ds){
8348         ds.on("beforeload", this.beforeLoad, this);
8349         ds.on("load", this.onLoad, this);
8350         ds.on("loadexception", this.onLoadError, this);
8351         ds.on("remove", this.updateInfo, this);
8352         ds.on("add", this.updateInfo, this);
8353         this.ds = ds;
8354     }
8355 });/*
8356  * Based on:
8357  * Ext JS Library 1.1.1
8358  * Copyright(c) 2006-2007, Ext JS, LLC.
8359  *
8360  * Originally Released Under LGPL - original licence link has changed is not relivant.
8361  *
8362  * Fork - LGPL
8363  * <script type="text/javascript">
8364  */
8365
8366 /**
8367  * @class Roo.Resizable
8368  * @extends Roo.util.Observable
8369  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
8370  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
8371  * the textarea in a div and set "resizeChild" to true (or to the id of the element), <b>or</b> set wrap:true in your config and
8372  * the element will be wrapped for you automatically.</p>
8373  * <p>Here is the list of valid resize handles:</p>
8374  * <pre>
8375 Value   Description
8376 ------  -------------------
8377  'n'     north
8378  's'     south
8379  'e'     east
8380  'w'     west
8381  'nw'    northwest
8382  'sw'    southwest
8383  'se'    southeast
8384  'ne'    northeast
8385  'hd'    horizontal drag
8386  'all'   all
8387 </pre>
8388  * <p>Here's an example showing the creation of a typical Resizable:</p>
8389  * <pre><code>
8390 var resizer = new Roo.Resizable("element-id", {
8391     handles: 'all',
8392     minWidth: 200,
8393     minHeight: 100,
8394     maxWidth: 500,
8395     maxHeight: 400,
8396     pinned: true
8397 });
8398 resizer.on("resize", myHandler);
8399 </code></pre>
8400  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
8401  * resizer.east.setDisplayed(false);</p>
8402  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
8403  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
8404  * resize operation's new size (defaults to [0, 0])
8405  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
8406  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
8407  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
8408  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
8409  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
8410  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
8411  * @cfg {Number} width The width of the element in pixels (defaults to null)
8412  * @cfg {Number} height The height of the element in pixels (defaults to null)
8413  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
8414  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
8415  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
8416  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
8417  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
8418  * in favor of the handles config option (defaults to false)
8419  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
8420  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
8421  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
8422  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
8423  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
8424  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
8425  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
8426  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
8427  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
8428  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
8429  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
8430  * @constructor
8431  * Create a new resizable component
8432  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
8433  * @param {Object} config configuration options
8434   */
8435 Roo.Resizable = function(el, config)
8436 {
8437     this.el = Roo.get(el);
8438
8439     if(config && config.wrap){
8440         config.resizeChild = this.el;
8441         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
8442         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
8443         this.el.setStyle("overflow", "hidden");
8444         this.el.setPositioning(config.resizeChild.getPositioning());
8445         config.resizeChild.clearPositioning();
8446         if(!config.width || !config.height){
8447             var csize = config.resizeChild.getSize();
8448             this.el.setSize(csize.width, csize.height);
8449         }
8450         if(config.pinned && !config.adjustments){
8451             config.adjustments = "auto";
8452         }
8453     }
8454
8455     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
8456     this.proxy.unselectable();
8457     this.proxy.enableDisplayMode('block');
8458
8459     Roo.apply(this, config);
8460
8461     if(this.pinned){
8462         this.disableTrackOver = true;
8463         this.el.addClass("x-resizable-pinned");
8464     }
8465     // if the element isn't positioned, make it relative
8466     var position = this.el.getStyle("position");
8467     if(position != "absolute" && position != "fixed"){
8468         this.el.setStyle("position", "relative");
8469     }
8470     if(!this.handles){ // no handles passed, must be legacy style
8471         this.handles = 's,e,se';
8472         if(this.multiDirectional){
8473             this.handles += ',n,w';
8474         }
8475     }
8476     if(this.handles == "all"){
8477         this.handles = "n s e w ne nw se sw";
8478     }
8479     var hs = this.handles.split(/\s*?[,;]\s*?| /);
8480     var ps = Roo.Resizable.positions;
8481     for(var i = 0, len = hs.length; i < len; i++){
8482         if(hs[i] && ps[hs[i]]){
8483             var pos = ps[hs[i]];
8484             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
8485         }
8486     }
8487     // legacy
8488     this.corner = this.southeast;
8489     
8490     // updateBox = the box can move..
8491     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
8492         this.updateBox = true;
8493     }
8494
8495     this.activeHandle = null;
8496
8497     if(this.resizeChild){
8498         if(typeof this.resizeChild == "boolean"){
8499             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
8500         }else{
8501             this.resizeChild = Roo.get(this.resizeChild, true);
8502         }
8503     }
8504     
8505     if(this.adjustments == "auto"){
8506         var rc = this.resizeChild;
8507         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
8508         if(rc && (hw || hn)){
8509             rc.position("relative");
8510             rc.setLeft(hw ? hw.el.getWidth() : 0);
8511             rc.setTop(hn ? hn.el.getHeight() : 0);
8512         }
8513         this.adjustments = [
8514             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
8515             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
8516         ];
8517     }
8518
8519     if(this.draggable){
8520         this.dd = this.dynamic ?
8521             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
8522         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
8523     }
8524
8525     // public events
8526     this.addEvents({
8527         /**
8528          * @event beforeresize
8529          * Fired before resize is allowed. Set enabled to false to cancel resize.
8530          * @param {Roo.Resizable} this
8531          * @param {Roo.EventObject} e The mousedown event
8532          */
8533         "beforeresize" : true,
8534         /**
8535          * @event resizing
8536          * Fired a resizing.
8537          * @param {Roo.Resizable} this
8538          * @param {Number} x The new x position
8539          * @param {Number} y The new y position
8540          * @param {Number} w The new w width
8541          * @param {Number} h The new h hight
8542          * @param {Roo.EventObject} e The mouseup event
8543          */
8544         "resizing" : true,
8545         /**
8546          * @event resize
8547          * Fired after a resize.
8548          * @param {Roo.Resizable} this
8549          * @param {Number} width The new width
8550          * @param {Number} height The new height
8551          * @param {Roo.EventObject} e The mouseup event
8552          */
8553         "resize" : true
8554     });
8555
8556     if(this.width !== null && this.height !== null){
8557         this.resizeTo(this.width, this.height);
8558     }else{
8559         this.updateChildSize();
8560     }
8561     if(Roo.isIE){
8562         this.el.dom.style.zoom = 1;
8563     }
8564     Roo.Resizable.superclass.constructor.call(this);
8565 };
8566
8567 Roo.extend(Roo.Resizable, Roo.util.Observable, {
8568         resizeChild : false,
8569         adjustments : [0, 0],
8570         minWidth : 5,
8571         minHeight : 5,
8572         maxWidth : 10000,
8573         maxHeight : 10000,
8574         enabled : true,
8575         animate : false,
8576         duration : .35,
8577         dynamic : false,
8578         handles : false,
8579         multiDirectional : false,
8580         disableTrackOver : false,
8581         easing : 'easeOutStrong',
8582         widthIncrement : 0,
8583         heightIncrement : 0,
8584         pinned : false,
8585         width : null,
8586         height : null,
8587         preserveRatio : false,
8588         transparent: false,
8589         minX: 0,
8590         minY: 0,
8591         draggable: false,
8592
8593         /**
8594          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
8595          */
8596         constrainTo: undefined,
8597         /**
8598          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
8599          */
8600         resizeRegion: undefined,
8601
8602
8603     /**
8604      * Perform a manual resize
8605      * @param {Number} width
8606      * @param {Number} height
8607      */
8608     resizeTo : function(width, height){
8609         this.el.setSize(width, height);
8610         this.updateChildSize();
8611         this.fireEvent("resize", this, width, height, null);
8612     },
8613
8614     // private
8615     startSizing : function(e, handle){
8616         this.fireEvent("beforeresize", this, e);
8617         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
8618
8619             if(!this.overlay){
8620                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
8621                 this.overlay.unselectable();
8622                 this.overlay.enableDisplayMode("block");
8623                 this.overlay.on("mousemove", this.onMouseMove, this);
8624                 this.overlay.on("mouseup", this.onMouseUp, this);
8625             }
8626             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
8627
8628             this.resizing = true;
8629             this.startBox = this.el.getBox();
8630             this.startPoint = e.getXY();
8631             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
8632                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
8633
8634             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8635             this.overlay.show();
8636
8637             if(this.constrainTo) {
8638                 var ct = Roo.get(this.constrainTo);
8639                 this.resizeRegion = ct.getRegion().adjust(
8640                     ct.getFrameWidth('t'),
8641                     ct.getFrameWidth('l'),
8642                     -ct.getFrameWidth('b'),
8643                     -ct.getFrameWidth('r')
8644                 );
8645             }
8646
8647             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
8648             this.proxy.show();
8649             this.proxy.setBox(this.startBox);
8650             if(!this.dynamic){
8651                 this.proxy.setStyle('visibility', 'visible');
8652             }
8653         }
8654     },
8655
8656     // private
8657     onMouseDown : function(handle, e){
8658         if(this.enabled){
8659             e.stopEvent();
8660             this.activeHandle = handle;
8661             this.startSizing(e, handle);
8662         }
8663     },
8664
8665     // private
8666     onMouseUp : function(e){
8667         var size = this.resizeElement();
8668         this.resizing = false;
8669         this.handleOut();
8670         this.overlay.hide();
8671         this.proxy.hide();
8672         this.fireEvent("resize", this, size.width, size.height, e);
8673     },
8674
8675     // private
8676     updateChildSize : function(){
8677         
8678         if(this.resizeChild){
8679             var el = this.el;
8680             var child = this.resizeChild;
8681             var adj = this.adjustments;
8682             if(el.dom.offsetWidth){
8683                 var b = el.getSize(true);
8684                 child.setSize(b.width+adj[0], b.height+adj[1]);
8685             }
8686             // Second call here for IE
8687             // The first call enables instant resizing and
8688             // the second call corrects scroll bars if they
8689             // exist
8690             if(Roo.isIE){
8691                 setTimeout(function(){
8692                     if(el.dom.offsetWidth){
8693                         var b = el.getSize(true);
8694                         child.setSize(b.width+adj[0], b.height+adj[1]);
8695                     }
8696                 }, 10);
8697             }
8698         }
8699     },
8700
8701     // private
8702     snap : function(value, inc, min){
8703         if(!inc || !value) {
8704             return value;
8705         }
8706         var newValue = value;
8707         var m = value % inc;
8708         if(m > 0){
8709             if(m > (inc/2)){
8710                 newValue = value + (inc-m);
8711             }else{
8712                 newValue = value - m;
8713             }
8714         }
8715         return Math.max(min, newValue);
8716     },
8717
8718     // private
8719     resizeElement : function(){
8720         var box = this.proxy.getBox();
8721         if(this.updateBox){
8722             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
8723         }else{
8724             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
8725         }
8726         this.updateChildSize();
8727         if(!this.dynamic){
8728             this.proxy.hide();
8729         }
8730         return box;
8731     },
8732
8733     // private
8734     constrain : function(v, diff, m, mx){
8735         if(v - diff < m){
8736             diff = v - m;
8737         }else if(v - diff > mx){
8738             diff = mx - v;
8739         }
8740         return diff;
8741     },
8742
8743     // private
8744     onMouseMove : function(e){
8745         
8746         if(this.enabled){
8747             try{// try catch so if something goes wrong the user doesn't get hung
8748
8749             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
8750                 return;
8751             }
8752
8753             //var curXY = this.startPoint;
8754             var curSize = this.curSize || this.startBox;
8755             var x = this.startBox.x, y = this.startBox.y;
8756             var ox = x, oy = y;
8757             var w = curSize.width, h = curSize.height;
8758             var ow = w, oh = h;
8759             var mw = this.minWidth, mh = this.minHeight;
8760             var mxw = this.maxWidth, mxh = this.maxHeight;
8761             var wi = this.widthIncrement;
8762             var hi = this.heightIncrement;
8763
8764             var eventXY = e.getXY();
8765             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
8766             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
8767
8768             var pos = this.activeHandle.position;
8769
8770             switch(pos){
8771                 case "east":
8772                     w += diffX;
8773                     w = Math.min(Math.max(mw, w), mxw);
8774                     break;
8775              
8776                 case "south":
8777                     h += diffY;
8778                     h = Math.min(Math.max(mh, h), mxh);
8779                     break;
8780                 case "southeast":
8781                     w += diffX;
8782                     h += diffY;
8783                     w = Math.min(Math.max(mw, w), mxw);
8784                     h = Math.min(Math.max(mh, h), mxh);
8785                     break;
8786                 case "north":
8787                     diffY = this.constrain(h, diffY, mh, mxh);
8788                     y += diffY;
8789                     h -= diffY;
8790                     break;
8791                 case "hdrag":
8792                     
8793                     if (wi) {
8794                         var adiffX = Math.abs(diffX);
8795                         var sub = (adiffX % wi); // how much 
8796                         if (sub > (wi/2)) { // far enough to snap
8797                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
8798                         } else {
8799                             // remove difference.. 
8800                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
8801                         }
8802                     }
8803                     x += diffX;
8804                     x = Math.max(this.minX, x);
8805                     break;
8806                 case "west":
8807                     diffX = this.constrain(w, diffX, mw, mxw);
8808                     x += diffX;
8809                     w -= diffX;
8810                     break;
8811                 case "northeast":
8812                     w += diffX;
8813                     w = Math.min(Math.max(mw, w), mxw);
8814                     diffY = this.constrain(h, diffY, mh, mxh);
8815                     y += diffY;
8816                     h -= diffY;
8817                     break;
8818                 case "northwest":
8819                     diffX = this.constrain(w, diffX, mw, mxw);
8820                     diffY = this.constrain(h, diffY, mh, mxh);
8821                     y += diffY;
8822                     h -= diffY;
8823                     x += diffX;
8824                     w -= diffX;
8825                     break;
8826                case "southwest":
8827                     diffX = this.constrain(w, diffX, mw, mxw);
8828                     h += diffY;
8829                     h = Math.min(Math.max(mh, h), mxh);
8830                     x += diffX;
8831                     w -= diffX;
8832                     break;
8833             }
8834
8835             var sw = this.snap(w, wi, mw);
8836             var sh = this.snap(h, hi, mh);
8837             if(sw != w || sh != h){
8838                 switch(pos){
8839                     case "northeast":
8840                         y -= sh - h;
8841                     break;
8842                     case "north":
8843                         y -= sh - h;
8844                         break;
8845                     case "southwest":
8846                         x -= sw - w;
8847                     break;
8848                     case "west":
8849                         x -= sw - w;
8850                         break;
8851                     case "northwest":
8852                         x -= sw - w;
8853                         y -= sh - h;
8854                     break;
8855                 }
8856                 w = sw;
8857                 h = sh;
8858             }
8859
8860             if(this.preserveRatio){
8861                 switch(pos){
8862                     case "southeast":
8863                     case "east":
8864                         h = oh * (w/ow);
8865                         h = Math.min(Math.max(mh, h), mxh);
8866                         w = ow * (h/oh);
8867                        break;
8868                     case "south":
8869                         w = ow * (h/oh);
8870                         w = Math.min(Math.max(mw, w), mxw);
8871                         h = oh * (w/ow);
8872                         break;
8873                     case "northeast":
8874                         w = ow * (h/oh);
8875                         w = Math.min(Math.max(mw, w), mxw);
8876                         h = oh * (w/ow);
8877                     break;
8878                     case "north":
8879                         var tw = w;
8880                         w = ow * (h/oh);
8881                         w = Math.min(Math.max(mw, w), mxw);
8882                         h = oh * (w/ow);
8883                         x += (tw - w) / 2;
8884                         break;
8885                     case "southwest":
8886                         h = oh * (w/ow);
8887                         h = Math.min(Math.max(mh, h), mxh);
8888                         var tw = w;
8889                         w = ow * (h/oh);
8890                         x += tw - w;
8891                         break;
8892                     case "west":
8893                         var th = h;
8894                         h = oh * (w/ow);
8895                         h = Math.min(Math.max(mh, h), mxh);
8896                         y += (th - h) / 2;
8897                         var tw = w;
8898                         w = ow * (h/oh);
8899                         x += tw - w;
8900                        break;
8901                     case "northwest":
8902                         var tw = w;
8903                         var th = h;
8904                         h = oh * (w/ow);
8905                         h = Math.min(Math.max(mh, h), mxh);
8906                         w = ow * (h/oh);
8907                         y += th - h;
8908                         x += tw - w;
8909                        break;
8910
8911                 }
8912             }
8913             if (pos == 'hdrag') {
8914                 w = ow;
8915             }
8916             this.proxy.setBounds(x, y, w, h);
8917             if(this.dynamic){
8918                 this.resizeElement();
8919             }
8920             }catch(e){}
8921         }
8922         this.fireEvent("resizing", this, x, y, w, h, e);
8923     },
8924
8925     // private
8926     handleOver : function(){
8927         if(this.enabled){
8928             this.el.addClass("x-resizable-over");
8929         }
8930     },
8931
8932     // private
8933     handleOut : function(){
8934         if(!this.resizing){
8935             this.el.removeClass("x-resizable-over");
8936         }
8937     },
8938
8939     /**
8940      * Returns the element this component is bound to.
8941      * @return {Roo.Element}
8942      */
8943     getEl : function(){
8944         return this.el;
8945     },
8946
8947     /**
8948      * Returns the resizeChild element (or null).
8949      * @return {Roo.Element}
8950      */
8951     getResizeChild : function(){
8952         return this.resizeChild;
8953     },
8954     groupHandler : function()
8955     {
8956         
8957     },
8958     /**
8959      * Destroys this resizable. If the element was wrapped and
8960      * removeEl is not true then the element remains.
8961      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
8962      */
8963     destroy : function(removeEl){
8964         this.proxy.remove();
8965         if(this.overlay){
8966             this.overlay.removeAllListeners();
8967             this.overlay.remove();
8968         }
8969         var ps = Roo.Resizable.positions;
8970         for(var k in ps){
8971             if(typeof ps[k] != "function" && this[ps[k]]){
8972                 var h = this[ps[k]];
8973                 h.el.removeAllListeners();
8974                 h.el.remove();
8975             }
8976         }
8977         if(removeEl){
8978             this.el.update("");
8979             this.el.remove();
8980         }
8981     }
8982 });
8983
8984 // private
8985 // hash to map config positions to true positions
8986 Roo.Resizable.positions = {
8987     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
8988     hd: "hdrag"
8989 };
8990
8991 // private
8992 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
8993     if(!this.tpl){
8994         // only initialize the template if resizable is used
8995         var tpl = Roo.DomHelper.createTemplate(
8996             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
8997         );
8998         tpl.compile();
8999         Roo.Resizable.Handle.prototype.tpl = tpl;
9000     }
9001     this.position = pos;
9002     this.rz = rz;
9003     // show north drag fro topdra
9004     var handlepos = pos == 'hdrag' ? 'north' : pos;
9005     
9006     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
9007     if (pos == 'hdrag') {
9008         this.el.setStyle('cursor', 'pointer');
9009     }
9010     this.el.unselectable();
9011     if(transparent){
9012         this.el.setOpacity(0);
9013     }
9014     this.el.on("mousedown", this.onMouseDown, this);
9015     if(!disableTrackOver){
9016         this.el.on("mouseover", this.onMouseOver, this);
9017         this.el.on("mouseout", this.onMouseOut, this);
9018     }
9019 };
9020
9021 // private
9022 Roo.Resizable.Handle.prototype = {
9023     afterResize : function(rz){
9024         Roo.log('after?');
9025         // do nothing
9026     },
9027     // private
9028     onMouseDown : function(e){
9029         this.rz.onMouseDown(this, e);
9030     },
9031     // private
9032     onMouseOver : function(e){
9033         this.rz.handleOver(this, e);
9034     },
9035     // private
9036     onMouseOut : function(e){
9037         this.rz.handleOut(this, e);
9038     }
9039 };/*
9040  * Based on:
9041  * Ext JS Library 1.1.1
9042  * Copyright(c) 2006-2007, Ext JS, LLC.
9043  *
9044  * Originally Released Under LGPL - original licence link has changed is not relivant.
9045  *
9046  * Fork - LGPL
9047  * <script type="text/javascript">
9048  */
9049
9050 /**
9051  * @class Roo.Editor
9052  * @extends Roo.Component
9053  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
9054  * @constructor
9055  * Create a new Editor
9056  * @param {Roo.form.Field} field The Field object (or descendant)
9057  * @param {Object} config The config object
9058  */
9059 Roo.Editor = function(field, config){
9060     Roo.Editor.superclass.constructor.call(this, config);
9061     this.field = field;
9062     this.addEvents({
9063         /**
9064              * @event beforestartedit
9065              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
9066              * false from the handler of this event.
9067              * @param {Editor} this
9068              * @param {Roo.Element} boundEl The underlying element bound to this editor
9069              * @param {Mixed} value The field value being set
9070              */
9071         "beforestartedit" : true,
9072         /**
9073              * @event startedit
9074              * Fires when this editor is displayed
9075              * @param {Roo.Element} boundEl The underlying element bound to this editor
9076              * @param {Mixed} value The starting field value
9077              */
9078         "startedit" : true,
9079         /**
9080              * @event beforecomplete
9081              * Fires after a change has been made to the field, but before the change is reflected in the underlying
9082              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
9083              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
9084              * event will not fire since no edit actually occurred.
9085              * @param {Editor} this
9086              * @param {Mixed} value The current field value
9087              * @param {Mixed} startValue The original field value
9088              */
9089         "beforecomplete" : true,
9090         /**
9091              * @event complete
9092              * Fires after editing is complete and any changed value has been written to the underlying field.
9093              * @param {Editor} this
9094              * @param {Mixed} value The current field value
9095              * @param {Mixed} startValue The original field value
9096              */
9097         "complete" : true,
9098         /**
9099          * @event specialkey
9100          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
9101          * {@link Roo.EventObject#getKey} to determine which key was pressed.
9102          * @param {Roo.form.Field} this
9103          * @param {Roo.EventObject} e The event object
9104          */
9105         "specialkey" : true
9106     });
9107 };
9108
9109 Roo.extend(Roo.Editor, Roo.Component, {
9110     /**
9111      * @cfg {Boolean/String} autosize
9112      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
9113      * or "height" to adopt the height only (defaults to false)
9114      */
9115     /**
9116      * @cfg {Boolean} revertInvalid
9117      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
9118      * validation fails (defaults to true)
9119      */
9120     /**
9121      * @cfg {Boolean} ignoreNoChange
9122      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
9123      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
9124      * will never be ignored.
9125      */
9126     /**
9127      * @cfg {Boolean} hideEl
9128      * False to keep the bound element visible while the editor is displayed (defaults to true)
9129      */
9130     /**
9131      * @cfg {Mixed} value
9132      * The data value of the underlying field (defaults to "")
9133      */
9134     value : "",
9135     /**
9136      * @cfg {String} alignment
9137      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
9138      */
9139     alignment: "c-c?",
9140     /**
9141      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
9142      * for bottom-right shadow (defaults to "frame")
9143      */
9144     shadow : "frame",
9145     /**
9146      * @cfg {Boolean} constrain True to constrain the editor to the viewport
9147      */
9148     constrain : false,
9149     /**
9150      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
9151      */
9152     completeOnEnter : false,
9153     /**
9154      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
9155      */
9156     cancelOnEsc : false,
9157     /**
9158      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
9159      */
9160     updateEl : false,
9161
9162     // private
9163     onRender : function(ct, position){
9164         this.el = new Roo.Layer({
9165             shadow: this.shadow,
9166             cls: "x-editor",
9167             parentEl : ct,
9168             shim : this.shim,
9169             shadowOffset:4,
9170             id: this.id,
9171             constrain: this.constrain
9172         });
9173         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
9174         if(this.field.msgTarget != 'title'){
9175             this.field.msgTarget = 'qtip';
9176         }
9177         this.field.render(this.el);
9178         if(Roo.isGecko){
9179             this.field.el.dom.setAttribute('autocomplete', 'off');
9180         }
9181         this.field.on("specialkey", this.onSpecialKey, this);
9182         if(this.swallowKeys){
9183             this.field.el.swallowEvent(['keydown','keypress']);
9184         }
9185         this.field.show();
9186         this.field.on("blur", this.onBlur, this);
9187         if(this.field.grow){
9188             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
9189         }
9190     },
9191
9192     onSpecialKey : function(field, e)
9193     {
9194         //Roo.log('editor onSpecialKey');
9195         if(this.completeOnEnter && e.getKey() == e.ENTER){
9196             e.stopEvent();
9197             this.completeEdit();
9198             return;
9199         }
9200         // do not fire special key otherwise it might hide close the editor...
9201         if(e.getKey() == e.ENTER){    
9202             return;
9203         }
9204         if(this.cancelOnEsc && e.getKey() == e.ESC){
9205             this.cancelEdit();
9206             return;
9207         } 
9208         this.fireEvent('specialkey', field, e);
9209     
9210     },
9211
9212     /**
9213      * Starts the editing process and shows the editor.
9214      * @param {String/HTMLElement/Element} el The element to edit
9215      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
9216       * to the innerHTML of el.
9217      */
9218     startEdit : function(el, value){
9219         if(this.editing){
9220             this.completeEdit();
9221         }
9222         this.boundEl = Roo.get(el);
9223         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
9224         if(!this.rendered){
9225             this.render(this.parentEl || document.body);
9226         }
9227         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
9228             return;
9229         }
9230         this.startValue = v;
9231         this.field.setValue(v);
9232         if(this.autoSize){
9233             var sz = this.boundEl.getSize();
9234             switch(this.autoSize){
9235                 case "width":
9236                 this.setSize(sz.width,  "");
9237                 break;
9238                 case "height":
9239                 this.setSize("",  sz.height);
9240                 break;
9241                 default:
9242                 this.setSize(sz.width,  sz.height);
9243             }
9244         }
9245         this.el.alignTo(this.boundEl, this.alignment);
9246         this.editing = true;
9247         if(Roo.QuickTips){
9248             Roo.QuickTips.disable();
9249         }
9250         this.show();
9251     },
9252
9253     /**
9254      * Sets the height and width of this editor.
9255      * @param {Number} width The new width
9256      * @param {Number} height The new height
9257      */
9258     setSize : function(w, h){
9259         this.field.setSize(w, h);
9260         if(this.el){
9261             this.el.sync();
9262         }
9263     },
9264
9265     /**
9266      * Realigns the editor to the bound field based on the current alignment config value.
9267      */
9268     realign : function(){
9269         this.el.alignTo(this.boundEl, this.alignment);
9270     },
9271
9272     /**
9273      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
9274      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
9275      */
9276     completeEdit : function(remainVisible){
9277         if(!this.editing){
9278             return;
9279         }
9280         var v = this.getValue();
9281         if(this.revertInvalid !== false && !this.field.isValid()){
9282             v = this.startValue;
9283             this.cancelEdit(true);
9284         }
9285         if(String(v) === String(this.startValue) && this.ignoreNoChange){
9286             this.editing = false;
9287             this.hide();
9288             return;
9289         }
9290         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
9291             this.editing = false;
9292             if(this.updateEl && this.boundEl){
9293                 this.boundEl.update(v);
9294             }
9295             if(remainVisible !== true){
9296                 this.hide();
9297             }
9298             this.fireEvent("complete", this, v, this.startValue);
9299         }
9300     },
9301
9302     // private
9303     onShow : function(){
9304         this.el.show();
9305         if(this.hideEl !== false){
9306             this.boundEl.hide();
9307         }
9308         this.field.show();
9309         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
9310             this.fixIEFocus = true;
9311             this.deferredFocus.defer(50, this);
9312         }else{
9313             this.field.focus();
9314         }
9315         this.fireEvent("startedit", this.boundEl, this.startValue);
9316     },
9317
9318     deferredFocus : function(){
9319         if(this.editing){
9320             this.field.focus();
9321         }
9322     },
9323
9324     /**
9325      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
9326      * reverted to the original starting value.
9327      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
9328      * cancel (defaults to false)
9329      */
9330     cancelEdit : function(remainVisible){
9331         if(this.editing){
9332             this.setValue(this.startValue);
9333             if(remainVisible !== true){
9334                 this.hide();
9335             }
9336         }
9337     },
9338
9339     // private
9340     onBlur : function(){
9341         if(this.allowBlur !== true && this.editing){
9342             this.completeEdit();
9343         }
9344     },
9345
9346     // private
9347     onHide : function(){
9348         if(this.editing){
9349             this.completeEdit();
9350             return;
9351         }
9352         this.field.blur();
9353         if(this.field.collapse){
9354             this.field.collapse();
9355         }
9356         this.el.hide();
9357         if(this.hideEl !== false){
9358             this.boundEl.show();
9359         }
9360         if(Roo.QuickTips){
9361             Roo.QuickTips.enable();
9362         }
9363     },
9364
9365     /**
9366      * Sets the data value of the editor
9367      * @param {Mixed} value Any valid value supported by the underlying field
9368      */
9369     setValue : function(v){
9370         this.field.setValue(v);
9371     },
9372
9373     /**
9374      * Gets the data value of the editor
9375      * @return {Mixed} The data value
9376      */
9377     getValue : function(){
9378         return this.field.getValue();
9379     }
9380 });/*
9381  * Based on:
9382  * Ext JS Library 1.1.1
9383  * Copyright(c) 2006-2007, Ext JS, LLC.
9384  *
9385  * Originally Released Under LGPL - original licence link has changed is not relivant.
9386  *
9387  * Fork - LGPL
9388  * <script type="text/javascript">
9389  */
9390  
9391 /**
9392  * @class Roo.BasicDialog
9393  * @extends Roo.util.Observable
9394  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
9395  * <pre><code>
9396 var dlg = new Roo.BasicDialog("my-dlg", {
9397     height: 200,
9398     width: 300,
9399     minHeight: 100,
9400     minWidth: 150,
9401     modal: true,
9402     proxyDrag: true,
9403     shadow: true
9404 });
9405 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
9406 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
9407 dlg.addButton('Cancel', dlg.hide, dlg);
9408 dlg.show();
9409 </code></pre>
9410   <b>A Dialog should always be a direct child of the body element.</b>
9411  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
9412  * @cfg {String} title Default text to display in the title bar (defaults to null)
9413  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9414  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9415  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
9416  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
9417  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
9418  * (defaults to null with no animation)
9419  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
9420  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
9421  * property for valid values (defaults to 'all')
9422  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
9423  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
9424  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
9425  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
9426  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
9427  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
9428  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
9429  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
9430  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
9431  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
9432  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
9433  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
9434  * draggable = true (defaults to false)
9435  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
9436  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
9437  * shadow (defaults to false)
9438  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
9439  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
9440  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
9441  * @cfg {Array} buttons Array of buttons
9442  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
9443  * @constructor
9444  * Create a new BasicDialog.
9445  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
9446  * @param {Object} config Configuration options
9447  */
9448 Roo.BasicDialog = function(el, config){
9449     this.el = Roo.get(el);
9450     var dh = Roo.DomHelper;
9451     if(!this.el && config && config.autoCreate){
9452         if(typeof config.autoCreate == "object"){
9453             if(!config.autoCreate.id){
9454                 config.autoCreate.id = el;
9455             }
9456             this.el = dh.append(document.body,
9457                         config.autoCreate, true);
9458         }else{
9459             this.el = dh.append(document.body,
9460                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
9461         }
9462     }
9463     el = this.el;
9464     el.setDisplayed(true);
9465     el.hide = this.hideAction;
9466     this.id = el.id;
9467     el.addClass("x-dlg");
9468
9469     Roo.apply(this, config);
9470
9471     this.proxy = el.createProxy("x-dlg-proxy");
9472     this.proxy.hide = this.hideAction;
9473     this.proxy.setOpacity(.5);
9474     this.proxy.hide();
9475
9476     if(config.width){
9477         el.setWidth(config.width);
9478     }
9479     if(config.height){
9480         el.setHeight(config.height);
9481     }
9482     this.size = el.getSize();
9483     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
9484         this.xy = [config.x,config.y];
9485     }else{
9486         this.xy = el.getCenterXY(true);
9487     }
9488     /** The header element @type Roo.Element */
9489     this.header = el.child("> .x-dlg-hd");
9490     /** The body element @type Roo.Element */
9491     this.body = el.child("> .x-dlg-bd");
9492     /** The footer element @type Roo.Element */
9493     this.footer = el.child("> .x-dlg-ft");
9494
9495     if(!this.header){
9496         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
9497     }
9498     if(!this.body){
9499         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
9500     }
9501
9502     this.header.unselectable();
9503     if(this.title){
9504         this.header.update(this.title);
9505     }
9506     // this element allows the dialog to be focused for keyboard event
9507     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
9508     this.focusEl.swallowEvent("click", true);
9509
9510     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
9511
9512     // wrap the body and footer for special rendering
9513     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
9514     if(this.footer){
9515         this.bwrap.dom.appendChild(this.footer.dom);
9516     }
9517
9518     this.bg = this.el.createChild({
9519         tag: "div", cls:"x-dlg-bg",
9520         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
9521     });
9522     this.centerBg = this.bg.child("div.x-dlg-bg-center");
9523
9524
9525     if(this.autoScroll !== false && !this.autoTabs){
9526         this.body.setStyle("overflow", "auto");
9527     }
9528
9529     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
9530
9531     if(this.closable !== false){
9532         this.el.addClass("x-dlg-closable");
9533         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
9534         this.close.on("click", this.closeClick, this);
9535         this.close.addClassOnOver("x-dlg-close-over");
9536     }
9537     if(this.collapsible !== false){
9538         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
9539         this.collapseBtn.on("click", this.collapseClick, this);
9540         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
9541         this.header.on("dblclick", this.collapseClick, this);
9542     }
9543     if(this.resizable !== false){
9544         this.el.addClass("x-dlg-resizable");
9545         this.resizer = new Roo.Resizable(el, {
9546             minWidth: this.minWidth || 80,
9547             minHeight:this.minHeight || 80,
9548             handles: this.resizeHandles || "all",
9549             pinned: true
9550         });
9551         this.resizer.on("beforeresize", this.beforeResize, this);
9552         this.resizer.on("resize", this.onResize, this);
9553     }
9554     if(this.draggable !== false){
9555         el.addClass("x-dlg-draggable");
9556         if (!this.proxyDrag) {
9557             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
9558         }
9559         else {
9560             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
9561         }
9562         dd.setHandleElId(this.header.id);
9563         dd.endDrag = this.endMove.createDelegate(this);
9564         dd.startDrag = this.startMove.createDelegate(this);
9565         dd.onDrag = this.onDrag.createDelegate(this);
9566         dd.scroll = false;
9567         this.dd = dd;
9568     }
9569     if(this.modal){
9570         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
9571         this.mask.enableDisplayMode("block");
9572         this.mask.hide();
9573         this.el.addClass("x-dlg-modal");
9574     }
9575     if(this.shadow){
9576         this.shadow = new Roo.Shadow({
9577             mode : typeof this.shadow == "string" ? this.shadow : "sides",
9578             offset : this.shadowOffset
9579         });
9580     }else{
9581         this.shadowOffset = 0;
9582     }
9583     if(Roo.useShims && this.shim !== false){
9584         this.shim = this.el.createShim();
9585         this.shim.hide = this.hideAction;
9586         this.shim.hide();
9587     }else{
9588         this.shim = false;
9589     }
9590     if(this.autoTabs){
9591         this.initTabs();
9592     }
9593     if (this.buttons) { 
9594         var bts= this.buttons;
9595         this.buttons = [];
9596         Roo.each(bts, function(b) {
9597             this.addButton(b);
9598         }, this);
9599     }
9600     
9601     
9602     this.addEvents({
9603         /**
9604          * @event keydown
9605          * Fires when a key is pressed
9606          * @param {Roo.BasicDialog} this
9607          * @param {Roo.EventObject} e
9608          */
9609         "keydown" : true,
9610         /**
9611          * @event move
9612          * Fires when this dialog is moved by the user.
9613          * @param {Roo.BasicDialog} this
9614          * @param {Number} x The new page X
9615          * @param {Number} y The new page Y
9616          */
9617         "move" : true,
9618         /**
9619          * @event resize
9620          * Fires when this dialog is resized by the user.
9621          * @param {Roo.BasicDialog} this
9622          * @param {Number} width The new width
9623          * @param {Number} height The new height
9624          */
9625         "resize" : true,
9626         /**
9627          * @event beforehide
9628          * Fires before this dialog is hidden.
9629          * @param {Roo.BasicDialog} this
9630          */
9631         "beforehide" : true,
9632         /**
9633          * @event hide
9634          * Fires when this dialog is hidden.
9635          * @param {Roo.BasicDialog} this
9636          */
9637         "hide" : true,
9638         /**
9639          * @event beforeshow
9640          * Fires before this dialog is shown.
9641          * @param {Roo.BasicDialog} this
9642          */
9643         "beforeshow" : true,
9644         /**
9645          * @event show
9646          * Fires when this dialog is shown.
9647          * @param {Roo.BasicDialog} this
9648          */
9649         "show" : true
9650     });
9651     el.on("keydown", this.onKeyDown, this);
9652     el.on("mousedown", this.toFront, this);
9653     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
9654     this.el.hide();
9655     Roo.DialogManager.register(this);
9656     Roo.BasicDialog.superclass.constructor.call(this);
9657 };
9658
9659 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
9660     shadowOffset: Roo.isIE ? 6 : 5,
9661     minHeight: 80,
9662     minWidth: 200,
9663     minButtonWidth: 75,
9664     defaultButton: null,
9665     buttonAlign: "right",
9666     tabTag: 'div',
9667     firstShow: true,
9668
9669     /**
9670      * Sets the dialog title text
9671      * @param {String} text The title text to display
9672      * @return {Roo.BasicDialog} this
9673      */
9674     setTitle : function(text){
9675         this.header.update(text);
9676         return this;
9677     },
9678
9679     // private
9680     closeClick : function(){
9681         this.hide();
9682     },
9683
9684     // private
9685     collapseClick : function(){
9686         this[this.collapsed ? "expand" : "collapse"]();
9687     },
9688
9689     /**
9690      * Collapses the dialog to its minimized state (only the title bar is visible).
9691      * Equivalent to the user clicking the collapse dialog button.
9692      */
9693     collapse : function(){
9694         if(!this.collapsed){
9695             this.collapsed = true;
9696             this.el.addClass("x-dlg-collapsed");
9697             this.restoreHeight = this.el.getHeight();
9698             this.resizeTo(this.el.getWidth(), this.header.getHeight());
9699         }
9700     },
9701
9702     /**
9703      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
9704      * clicking the expand dialog button.
9705      */
9706     expand : function(){
9707         if(this.collapsed){
9708             this.collapsed = false;
9709             this.el.removeClass("x-dlg-collapsed");
9710             this.resizeTo(this.el.getWidth(), this.restoreHeight);
9711         }
9712     },
9713
9714     /**
9715      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
9716      * @return {Roo.TabPanel} The tabs component
9717      */
9718     initTabs : function(){
9719         var tabs = this.getTabs();
9720         while(tabs.getTab(0)){
9721             tabs.removeTab(0);
9722         }
9723         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
9724             var dom = el.dom;
9725             tabs.addTab(Roo.id(dom), dom.title);
9726             dom.title = "";
9727         });
9728         tabs.activate(0);
9729         return tabs;
9730     },
9731
9732     // private
9733     beforeResize : function(){
9734         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
9735     },
9736
9737     // private
9738     onResize : function(){
9739         this.refreshSize();
9740         this.syncBodyHeight();
9741         this.adjustAssets();
9742         this.focus();
9743         this.fireEvent("resize", this, this.size.width, this.size.height);
9744     },
9745
9746     // private
9747     onKeyDown : function(e){
9748         if(this.isVisible()){
9749             this.fireEvent("keydown", this, e);
9750         }
9751     },
9752
9753     /**
9754      * Resizes the dialog.
9755      * @param {Number} width
9756      * @param {Number} height
9757      * @return {Roo.BasicDialog} this
9758      */
9759     resizeTo : function(width, height){
9760         this.el.setSize(width, height);
9761         this.size = {width: width, height: height};
9762         this.syncBodyHeight();
9763         if(this.fixedcenter){
9764             this.center();
9765         }
9766         if(this.isVisible()){
9767             this.constrainXY();
9768             this.adjustAssets();
9769         }
9770         this.fireEvent("resize", this, width, height);
9771         return this;
9772     },
9773
9774
9775     /**
9776      * Resizes the dialog to fit the specified content size.
9777      * @param {Number} width
9778      * @param {Number} height
9779      * @return {Roo.BasicDialog} this
9780      */
9781     setContentSize : function(w, h){
9782         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
9783         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
9784         //if(!this.el.isBorderBox()){
9785             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
9786             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
9787         //}
9788         if(this.tabs){
9789             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
9790             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
9791         }
9792         this.resizeTo(w, h);
9793         return this;
9794     },
9795
9796     /**
9797      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
9798      * executed in response to a particular key being pressed while the dialog is active.
9799      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
9800      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9801      * @param {Function} fn The function to call
9802      * @param {Object} scope (optional) The scope of the function
9803      * @return {Roo.BasicDialog} this
9804      */
9805     addKeyListener : function(key, fn, scope){
9806         var keyCode, shift, ctrl, alt;
9807         if(typeof key == "object" && !(key instanceof Array)){
9808             keyCode = key["key"];
9809             shift = key["shift"];
9810             ctrl = key["ctrl"];
9811             alt = key["alt"];
9812         }else{
9813             keyCode = key;
9814         }
9815         var handler = function(dlg, e){
9816             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
9817                 var k = e.getKey();
9818                 if(keyCode instanceof Array){
9819                     for(var i = 0, len = keyCode.length; i < len; i++){
9820                         if(keyCode[i] == k){
9821                           fn.call(scope || window, dlg, k, e);
9822                           return;
9823                         }
9824                     }
9825                 }else{
9826                     if(k == keyCode){
9827                         fn.call(scope || window, dlg, k, e);
9828                     }
9829                 }
9830             }
9831         };
9832         this.on("keydown", handler);
9833         return this;
9834     },
9835
9836     /**
9837      * Returns the TabPanel component (creates it if it doesn't exist).
9838      * Note: If you wish to simply check for the existence of tabs without creating them,
9839      * check for a null 'tabs' property.
9840      * @return {Roo.TabPanel} The tabs component
9841      */
9842     getTabs : function(){
9843         if(!this.tabs){
9844             this.el.addClass("x-dlg-auto-tabs");
9845             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
9846             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
9847         }
9848         return this.tabs;
9849     },
9850
9851     /**
9852      * Adds a button to the footer section of the dialog.
9853      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
9854      * object or a valid Roo.DomHelper element config
9855      * @param {Function} handler The function called when the button is clicked
9856      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
9857      * @return {Roo.Button} The new button
9858      */
9859     addButton : function(config, handler, scope){
9860         var dh = Roo.DomHelper;
9861         if(!this.footer){
9862             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
9863         }
9864         if(!this.btnContainer){
9865             var tb = this.footer.createChild({
9866
9867                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
9868                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
9869             }, null, true);
9870             this.btnContainer = tb.firstChild.firstChild.firstChild;
9871         }
9872         var bconfig = {
9873             handler: handler,
9874             scope: scope,
9875             minWidth: this.minButtonWidth,
9876             hideParent:true
9877         };
9878         if(typeof config == "string"){
9879             bconfig.text = config;
9880         }else{
9881             if(config.tag){
9882                 bconfig.dhconfig = config;
9883             }else{
9884                 Roo.apply(bconfig, config);
9885             }
9886         }
9887         var fc = false;
9888         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
9889             bconfig.position = Math.max(0, bconfig.position);
9890             fc = this.btnContainer.childNodes[bconfig.position];
9891         }
9892          
9893         var btn = new Roo.Button(
9894             fc ? 
9895                 this.btnContainer.insertBefore(document.createElement("td"),fc)
9896                 : this.btnContainer.appendChild(document.createElement("td")),
9897             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
9898             bconfig
9899         );
9900         this.syncBodyHeight();
9901         if(!this.buttons){
9902             /**
9903              * Array of all the buttons that have been added to this dialog via addButton
9904              * @type Array
9905              */
9906             this.buttons = [];
9907         }
9908         this.buttons.push(btn);
9909         return btn;
9910     },
9911
9912     /**
9913      * Sets the default button to be focused when the dialog is displayed.
9914      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
9915      * @return {Roo.BasicDialog} this
9916      */
9917     setDefaultButton : function(btn){
9918         this.defaultButton = btn;
9919         return this;
9920     },
9921
9922     // private
9923     getHeaderFooterHeight : function(safe){
9924         var height = 0;
9925         if(this.header){
9926            height += this.header.getHeight();
9927         }
9928         if(this.footer){
9929            var fm = this.footer.getMargins();
9930             height += (this.footer.getHeight()+fm.top+fm.bottom);
9931         }
9932         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
9933         height += this.centerBg.getPadding("tb");
9934         return height;
9935     },
9936
9937     // private
9938     syncBodyHeight : function()
9939     {
9940         var bd = this.body, // the text
9941             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
9942             bw = this.bwrap;
9943         var height = this.size.height - this.getHeaderFooterHeight(false);
9944         bd.setHeight(height-bd.getMargins("tb"));
9945         var hh = this.header.getHeight();
9946         var h = this.size.height-hh;
9947         cb.setHeight(h);
9948         
9949         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
9950         bw.setHeight(h-cb.getPadding("tb"));
9951         
9952         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
9953         bd.setWidth(bw.getWidth(true));
9954         if(this.tabs){
9955             this.tabs.syncHeight();
9956             if(Roo.isIE){
9957                 this.tabs.el.repaint();
9958             }
9959         }
9960     },
9961
9962     /**
9963      * Restores the previous state of the dialog if Roo.state is configured.
9964      * @return {Roo.BasicDialog} this
9965      */
9966     restoreState : function(){
9967         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
9968         if(box && box.width){
9969             this.xy = [box.x, box.y];
9970             this.resizeTo(box.width, box.height);
9971         }
9972         return this;
9973     },
9974
9975     // private
9976     beforeShow : function(){
9977         this.expand();
9978         if(this.fixedcenter){
9979             this.xy = this.el.getCenterXY(true);
9980         }
9981         if(this.modal){
9982             Roo.get(document.body).addClass("x-body-masked");
9983             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
9984             this.mask.show();
9985         }
9986         this.constrainXY();
9987     },
9988
9989     // private
9990     animShow : function(){
9991         var b = Roo.get(this.animateTarget).getBox();
9992         this.proxy.setSize(b.width, b.height);
9993         this.proxy.setLocation(b.x, b.y);
9994         this.proxy.show();
9995         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
9996                     true, .35, this.showEl.createDelegate(this));
9997     },
9998
9999     /**
10000      * Shows the dialog.
10001      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
10002      * @return {Roo.BasicDialog} this
10003      */
10004     show : function(animateTarget){
10005         if (this.fireEvent("beforeshow", this) === false){
10006             return;
10007         }
10008         if(this.syncHeightBeforeShow){
10009             this.syncBodyHeight();
10010         }else if(this.firstShow){
10011             this.firstShow = false;
10012             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
10013         }
10014         this.animateTarget = animateTarget || this.animateTarget;
10015         if(!this.el.isVisible()){
10016             this.beforeShow();
10017             if(this.animateTarget && Roo.get(this.animateTarget)){
10018                 this.animShow();
10019             }else{
10020                 this.showEl();
10021             }
10022         }
10023         return this;
10024     },
10025
10026     // private
10027     showEl : function(){
10028         this.proxy.hide();
10029         this.el.setXY(this.xy);
10030         this.el.show();
10031         this.adjustAssets(true);
10032         this.toFront();
10033         this.focus();
10034         // IE peekaboo bug - fix found by Dave Fenwick
10035         if(Roo.isIE){
10036             this.el.repaint();
10037         }
10038         this.fireEvent("show", this);
10039     },
10040
10041     /**
10042      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
10043      * dialog itself will receive focus.
10044      */
10045     focus : function(){
10046         if(this.defaultButton){
10047             this.defaultButton.focus();
10048         }else{
10049             this.focusEl.focus();
10050         }
10051     },
10052
10053     // private
10054     constrainXY : function(){
10055         if(this.constraintoviewport !== false){
10056             if(!this.viewSize){
10057                 if(this.container){
10058                     var s = this.container.getSize();
10059                     this.viewSize = [s.width, s.height];
10060                 }else{
10061                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
10062                 }
10063             }
10064             var s = Roo.get(this.container||document).getScroll();
10065
10066             var x = this.xy[0], y = this.xy[1];
10067             var w = this.size.width, h = this.size.height;
10068             var vw = this.viewSize[0], vh = this.viewSize[1];
10069             // only move it if it needs it
10070             var moved = false;
10071             // first validate right/bottom
10072             if(x + w > vw+s.left){
10073                 x = vw - w;
10074                 moved = true;
10075             }
10076             if(y + h > vh+s.top){
10077                 y = vh - h;
10078                 moved = true;
10079             }
10080             // then make sure top/left isn't negative
10081             if(x < s.left){
10082                 x = s.left;
10083                 moved = true;
10084             }
10085             if(y < s.top){
10086                 y = s.top;
10087                 moved = true;
10088             }
10089             if(moved){
10090                 // cache xy
10091                 this.xy = [x, y];
10092                 if(this.isVisible()){
10093                     this.el.setLocation(x, y);
10094                     this.adjustAssets();
10095                 }
10096             }
10097         }
10098     },
10099
10100     // private
10101     onDrag : function(){
10102         if(!this.proxyDrag){
10103             this.xy = this.el.getXY();
10104             this.adjustAssets();
10105         }
10106     },
10107
10108     // private
10109     adjustAssets : function(doShow){
10110         var x = this.xy[0], y = this.xy[1];
10111         var w = this.size.width, h = this.size.height;
10112         if(doShow === true){
10113             if(this.shadow){
10114                 this.shadow.show(this.el);
10115             }
10116             if(this.shim){
10117                 this.shim.show();
10118             }
10119         }
10120         if(this.shadow && this.shadow.isVisible()){
10121             this.shadow.show(this.el);
10122         }
10123         if(this.shim && this.shim.isVisible()){
10124             this.shim.setBounds(x, y, w, h);
10125         }
10126     },
10127
10128     // private
10129     adjustViewport : function(w, h){
10130         if(!w || !h){
10131             w = Roo.lib.Dom.getViewWidth();
10132             h = Roo.lib.Dom.getViewHeight();
10133         }
10134         // cache the size
10135         this.viewSize = [w, h];
10136         if(this.modal && this.mask.isVisible()){
10137             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
10138             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
10139         }
10140         if(this.isVisible()){
10141             this.constrainXY();
10142         }
10143     },
10144
10145     /**
10146      * Destroys this dialog and all its supporting elements (including any tabs, shim,
10147      * shadow, proxy, mask, etc.)  Also removes all event listeners.
10148      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
10149      */
10150     destroy : function(removeEl){
10151         if(this.isVisible()){
10152             this.animateTarget = null;
10153             this.hide();
10154         }
10155         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
10156         if(this.tabs){
10157             this.tabs.destroy(removeEl);
10158         }
10159         Roo.destroy(
10160              this.shim,
10161              this.proxy,
10162              this.resizer,
10163              this.close,
10164              this.mask
10165         );
10166         if(this.dd){
10167             this.dd.unreg();
10168         }
10169         if(this.buttons){
10170            for(var i = 0, len = this.buttons.length; i < len; i++){
10171                this.buttons[i].destroy();
10172            }
10173         }
10174         this.el.removeAllListeners();
10175         if(removeEl === true){
10176             this.el.update("");
10177             this.el.remove();
10178         }
10179         Roo.DialogManager.unregister(this);
10180     },
10181
10182     // private
10183     startMove : function(){
10184         if(this.proxyDrag){
10185             this.proxy.show();
10186         }
10187         if(this.constraintoviewport !== false){
10188             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
10189         }
10190     },
10191
10192     // private
10193     endMove : function(){
10194         if(!this.proxyDrag){
10195             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
10196         }else{
10197             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
10198             this.proxy.hide();
10199         }
10200         this.refreshSize();
10201         this.adjustAssets();
10202         this.focus();
10203         this.fireEvent("move", this, this.xy[0], this.xy[1]);
10204     },
10205
10206     /**
10207      * Brings this dialog to the front of any other visible dialogs
10208      * @return {Roo.BasicDialog} this
10209      */
10210     toFront : function(){
10211         Roo.DialogManager.bringToFront(this);
10212         return this;
10213     },
10214
10215     /**
10216      * Sends this dialog to the back (under) of any other visible dialogs
10217      * @return {Roo.BasicDialog} this
10218      */
10219     toBack : function(){
10220         Roo.DialogManager.sendToBack(this);
10221         return this;
10222     },
10223
10224     /**
10225      * Centers this dialog in the viewport
10226      * @return {Roo.BasicDialog} this
10227      */
10228     center : function(){
10229         var xy = this.el.getCenterXY(true);
10230         this.moveTo(xy[0], xy[1]);
10231         return this;
10232     },
10233
10234     /**
10235      * Moves the dialog's top-left corner to the specified point
10236      * @param {Number} x
10237      * @param {Number} y
10238      * @return {Roo.BasicDialog} this
10239      */
10240     moveTo : function(x, y){
10241         this.xy = [x,y];
10242         if(this.isVisible()){
10243             this.el.setXY(this.xy);
10244             this.adjustAssets();
10245         }
10246         return this;
10247     },
10248
10249     /**
10250      * Aligns the dialog to the specified element
10251      * @param {String/HTMLElement/Roo.Element} element The element to align to.
10252      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
10253      * @param {Array} offsets (optional) Offset the positioning by [x, y]
10254      * @return {Roo.BasicDialog} this
10255      */
10256     alignTo : function(element, position, offsets){
10257         this.xy = this.el.getAlignToXY(element, position, offsets);
10258         if(this.isVisible()){
10259             this.el.setXY(this.xy);
10260             this.adjustAssets();
10261         }
10262         return this;
10263     },
10264
10265     /**
10266      * Anchors an element to another element and realigns it when the window is resized.
10267      * @param {String/HTMLElement/Roo.Element} element The element to align to.
10268      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
10269      * @param {Array} offsets (optional) Offset the positioning by [x, y]
10270      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
10271      * is a number, it is used as the buffer delay (defaults to 50ms).
10272      * @return {Roo.BasicDialog} this
10273      */
10274     anchorTo : function(el, alignment, offsets, monitorScroll){
10275         var action = function(){
10276             this.alignTo(el, alignment, offsets);
10277         };
10278         Roo.EventManager.onWindowResize(action, this);
10279         var tm = typeof monitorScroll;
10280         if(tm != 'undefined'){
10281             Roo.EventManager.on(window, 'scroll', action, this,
10282                 {buffer: tm == 'number' ? monitorScroll : 50});
10283         }
10284         action.call(this);
10285         return this;
10286     },
10287
10288     /**
10289      * Returns true if the dialog is visible
10290      * @return {Boolean}
10291      */
10292     isVisible : function(){
10293         return this.el.isVisible();
10294     },
10295
10296     // private
10297     animHide : function(callback){
10298         var b = Roo.get(this.animateTarget).getBox();
10299         this.proxy.show();
10300         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
10301         this.el.hide();
10302         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
10303                     this.hideEl.createDelegate(this, [callback]));
10304     },
10305
10306     /**
10307      * Hides the dialog.
10308      * @param {Function} callback (optional) Function to call when the dialog is hidden
10309      * @return {Roo.BasicDialog} this
10310      */
10311     hide : function(callback){
10312         if (this.fireEvent("beforehide", this) === false){
10313             return;
10314         }
10315         if(this.shadow){
10316             this.shadow.hide();
10317         }
10318         if(this.shim) {
10319           this.shim.hide();
10320         }
10321         // sometimes animateTarget seems to get set.. causing problems...
10322         // this just double checks..
10323         if(this.animateTarget && Roo.get(this.animateTarget)) {
10324            this.animHide(callback);
10325         }else{
10326             this.el.hide();
10327             this.hideEl(callback);
10328         }
10329         return this;
10330     },
10331
10332     // private
10333     hideEl : function(callback){
10334         this.proxy.hide();
10335         if(this.modal){
10336             this.mask.hide();
10337             Roo.get(document.body).removeClass("x-body-masked");
10338         }
10339         this.fireEvent("hide", this);
10340         if(typeof callback == "function"){
10341             callback();
10342         }
10343     },
10344
10345     // private
10346     hideAction : function(){
10347         this.setLeft("-10000px");
10348         this.setTop("-10000px");
10349         this.setStyle("visibility", "hidden");
10350     },
10351
10352     // private
10353     refreshSize : function(){
10354         this.size = this.el.getSize();
10355         this.xy = this.el.getXY();
10356         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
10357     },
10358
10359     // private
10360     // z-index is managed by the DialogManager and may be overwritten at any time
10361     setZIndex : function(index){
10362         if(this.modal){
10363             this.mask.setStyle("z-index", index);
10364         }
10365         if(this.shim){
10366             this.shim.setStyle("z-index", ++index);
10367         }
10368         if(this.shadow){
10369             this.shadow.setZIndex(++index);
10370         }
10371         this.el.setStyle("z-index", ++index);
10372         if(this.proxy){
10373             this.proxy.setStyle("z-index", ++index);
10374         }
10375         if(this.resizer){
10376             this.resizer.proxy.setStyle("z-index", ++index);
10377         }
10378
10379         this.lastZIndex = index;
10380     },
10381
10382     /**
10383      * Returns the element for this dialog
10384      * @return {Roo.Element} The underlying dialog Element
10385      */
10386     getEl : function(){
10387         return this.el;
10388     }
10389 });
10390
10391 /**
10392  * @class Roo.DialogManager
10393  * Provides global access to BasicDialogs that have been created and
10394  * support for z-indexing (layering) multiple open dialogs.
10395  */
10396 Roo.DialogManager = function(){
10397     var list = {};
10398     var accessList = [];
10399     var front = null;
10400
10401     // private
10402     var sortDialogs = function(d1, d2){
10403         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
10404     };
10405
10406     // private
10407     var orderDialogs = function(){
10408         accessList.sort(sortDialogs);
10409         var seed = Roo.DialogManager.zseed;
10410         for(var i = 0, len = accessList.length; i < len; i++){
10411             var dlg = accessList[i];
10412             if(dlg){
10413                 dlg.setZIndex(seed + (i*10));
10414             }
10415         }
10416     };
10417
10418     return {
10419         /**
10420          * The starting z-index for BasicDialogs (defaults to 9000)
10421          * @type Number The z-index value
10422          */
10423         zseed : 9000,
10424
10425         // private
10426         register : function(dlg){
10427             list[dlg.id] = dlg;
10428             accessList.push(dlg);
10429         },
10430
10431         // private
10432         unregister : function(dlg){
10433             delete list[dlg.id];
10434             var i=0;
10435             var len=0;
10436             if(!accessList.indexOf){
10437                 for(  i = 0, len = accessList.length; i < len; i++){
10438                     if(accessList[i] == dlg){
10439                         accessList.splice(i, 1);
10440                         return;
10441                     }
10442                 }
10443             }else{
10444                  i = accessList.indexOf(dlg);
10445                 if(i != -1){
10446                     accessList.splice(i, 1);
10447                 }
10448             }
10449         },
10450
10451         /**
10452          * Gets a registered dialog by id
10453          * @param {String/Object} id The id of the dialog or a dialog
10454          * @return {Roo.BasicDialog} this
10455          */
10456         get : function(id){
10457             return typeof id == "object" ? id : list[id];
10458         },
10459
10460         /**
10461          * Brings the specified dialog to the front
10462          * @param {String/Object} dlg The id of the dialog or a dialog
10463          * @return {Roo.BasicDialog} this
10464          */
10465         bringToFront : function(dlg){
10466             dlg = this.get(dlg);
10467             if(dlg != front){
10468                 front = dlg;
10469                 dlg._lastAccess = new Date().getTime();
10470                 orderDialogs();
10471             }
10472             return dlg;
10473         },
10474
10475         /**
10476          * Sends the specified dialog to the back
10477          * @param {String/Object} dlg The id of the dialog or a dialog
10478          * @return {Roo.BasicDialog} this
10479          */
10480         sendToBack : function(dlg){
10481             dlg = this.get(dlg);
10482             dlg._lastAccess = -(new Date().getTime());
10483             orderDialogs();
10484             return dlg;
10485         },
10486
10487         /**
10488          * Hides all dialogs
10489          */
10490         hideAll : function(){
10491             for(var id in list){
10492                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
10493                     list[id].hide();
10494                 }
10495             }
10496         }
10497     };
10498 }();
10499
10500 /**
10501  * @class Roo.LayoutDialog
10502  * @extends Roo.BasicDialog
10503  * Dialog which provides adjustments for working with a layout in a Dialog.
10504  * Add your necessary layout config options to the dialog's config.<br>
10505  * Example usage (including a nested layout):
10506  * <pre><code>
10507 if(!dialog){
10508     dialog = new Roo.LayoutDialog("download-dlg", {
10509         modal: true,
10510         width:600,
10511         height:450,
10512         shadow:true,
10513         minWidth:500,
10514         minHeight:350,
10515         autoTabs:true,
10516         proxyDrag:true,
10517         // layout config merges with the dialog config
10518         center:{
10519             tabPosition: "top",
10520             alwaysShowTabs: true
10521         }
10522     });
10523     dialog.addKeyListener(27, dialog.hide, dialog);
10524     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
10525     dialog.addButton("Build It!", this.getDownload, this);
10526
10527     // we can even add nested layouts
10528     var innerLayout = new Roo.BorderLayout("dl-inner", {
10529         east: {
10530             initialSize: 200,
10531             autoScroll:true,
10532             split:true
10533         },
10534         center: {
10535             autoScroll:true
10536         }
10537     });
10538     innerLayout.beginUpdate();
10539     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
10540     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
10541     innerLayout.endUpdate(true);
10542
10543     var layout = dialog.getLayout();
10544     layout.beginUpdate();
10545     layout.add("center", new Roo.ContentPanel("standard-panel",
10546                         {title: "Download the Source", fitToFrame:true}));
10547     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
10548                {title: "Build your own roo.js"}));
10549     layout.getRegion("center").showPanel(sp);
10550     layout.endUpdate();
10551 }
10552 </code></pre>
10553     * @constructor
10554     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
10555     * @param {Object} config configuration options
10556   */
10557 Roo.LayoutDialog = function(el, cfg){
10558     
10559     var config=  cfg;
10560     if (typeof(cfg) == 'undefined') {
10561         config = Roo.apply({}, el);
10562         // not sure why we use documentElement here.. - it should always be body.
10563         // IE7 borks horribly if we use documentElement.
10564         // webkit also does not like documentElement - it creates a body element...
10565         el = Roo.get( document.body || document.documentElement ).createChild();
10566         //config.autoCreate = true;
10567     }
10568     
10569     
10570     config.autoTabs = false;
10571     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
10572     this.body.setStyle({overflow:"hidden", position:"relative"});
10573     this.layout = new Roo.BorderLayout(this.body.dom, config);
10574     this.layout.monitorWindowResize = false;
10575     this.el.addClass("x-dlg-auto-layout");
10576     // fix case when center region overwrites center function
10577     this.center = Roo.BasicDialog.prototype.center;
10578     this.on("show", this.layout.layout, this.layout, true);
10579     if (config.items) {
10580         var xitems = config.items;
10581         delete config.items;
10582         Roo.each(xitems, this.addxtype, this);
10583     }
10584     
10585     
10586 };
10587 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
10588     /**
10589      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
10590      * @deprecated
10591      */
10592     endUpdate : function(){
10593         this.layout.endUpdate();
10594     },
10595
10596     /**
10597      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
10598      *  @deprecated
10599      */
10600     beginUpdate : function(){
10601         this.layout.beginUpdate();
10602     },
10603
10604     /**
10605      * Get the BorderLayout for this dialog
10606      * @return {Roo.BorderLayout}
10607      */
10608     getLayout : function(){
10609         return this.layout;
10610     },
10611
10612     showEl : function(){
10613         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
10614         if(Roo.isIE7){
10615             this.layout.layout();
10616         }
10617     },
10618
10619     // private
10620     // Use the syncHeightBeforeShow config option to control this automatically
10621     syncBodyHeight : function(){
10622         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
10623         if(this.layout){this.layout.layout();}
10624     },
10625     
10626       /**
10627      * Add an xtype element (actually adds to the layout.)
10628      * @return {Object} xdata xtype object data.
10629      */
10630     
10631     addxtype : function(c) {
10632         return this.layout.addxtype(c);
10633     }
10634 });/*
10635  * Based on:
10636  * Ext JS Library 1.1.1
10637  * Copyright(c) 2006-2007, Ext JS, LLC.
10638  *
10639  * Originally Released Under LGPL - original licence link has changed is not relivant.
10640  *
10641  * Fork - LGPL
10642  * <script type="text/javascript">
10643  */
10644  
10645 /**
10646  * @class Roo.MessageBox
10647  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
10648  * Example usage:
10649  *<pre><code>
10650 // Basic alert:
10651 Roo.Msg.alert('Status', 'Changes saved successfully.');
10652
10653 // Prompt for user data:
10654 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
10655     if (btn == 'ok'){
10656         // process text value...
10657     }
10658 });
10659
10660 // Show a dialog using config options:
10661 Roo.Msg.show({
10662    title:'Save Changes?',
10663    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
10664    buttons: Roo.Msg.YESNOCANCEL,
10665    fn: processResult,
10666    animEl: 'elId'
10667 });
10668 </code></pre>
10669  * @singleton
10670  */
10671 Roo.MessageBox = function(){
10672     var dlg, opt, mask, waitTimer;
10673     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
10674     var buttons, activeTextEl, bwidth;
10675
10676     // private
10677     var handleButton = function(button){
10678         dlg.hide();
10679         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
10680     };
10681
10682     // private
10683     var handleHide = function(){
10684         if(opt && opt.cls){
10685             dlg.el.removeClass(opt.cls);
10686         }
10687         if(waitTimer){
10688             Roo.TaskMgr.stop(waitTimer);
10689             waitTimer = null;
10690         }
10691     };
10692
10693     // private
10694     var updateButtons = function(b){
10695         var width = 0;
10696         if(!b){
10697             buttons["ok"].hide();
10698             buttons["cancel"].hide();
10699             buttons["yes"].hide();
10700             buttons["no"].hide();
10701             dlg.footer.dom.style.display = 'none';
10702             return width;
10703         }
10704         dlg.footer.dom.style.display = '';
10705         for(var k in buttons){
10706             if(typeof buttons[k] != "function"){
10707                 if(b[k]){
10708                     buttons[k].show();
10709                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
10710                     width += buttons[k].el.getWidth()+15;
10711                 }else{
10712                     buttons[k].hide();
10713                 }
10714             }
10715         }
10716         return width;
10717     };
10718
10719     // private
10720     var handleEsc = function(d, k, e){
10721         if(opt && opt.closable !== false){
10722             dlg.hide();
10723         }
10724         if(e){
10725             e.stopEvent();
10726         }
10727     };
10728
10729     return {
10730         /**
10731          * Returns a reference to the underlying {@link Roo.BasicDialog} element
10732          * @return {Roo.BasicDialog} The BasicDialog element
10733          */
10734         getDialog : function(){
10735            if(!dlg){
10736                 dlg = new Roo.BasicDialog("x-msg-box", {
10737                     autoCreate : true,
10738                     shadow: true,
10739                     draggable: true,
10740                     resizable:false,
10741                     constraintoviewport:false,
10742                     fixedcenter:true,
10743                     collapsible : false,
10744                     shim:true,
10745                     modal: true,
10746                     width:400, height:100,
10747                     buttonAlign:"center",
10748                     closeClick : function(){
10749                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
10750                             handleButton("no");
10751                         }else{
10752                             handleButton("cancel");
10753                         }
10754                     }
10755                 });
10756                 dlg.on("hide", handleHide);
10757                 mask = dlg.mask;
10758                 dlg.addKeyListener(27, handleEsc);
10759                 buttons = {};
10760                 var bt = this.buttonText;
10761                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
10762                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
10763                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
10764                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
10765                 bodyEl = dlg.body.createChild({
10766
10767                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" /><textarea class="roo-mb-textarea"></textarea><div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
10768                 });
10769                 msgEl = bodyEl.dom.firstChild;
10770                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
10771                 textboxEl.enableDisplayMode();
10772                 textboxEl.addKeyListener([10,13], function(){
10773                     if(dlg.isVisible() && opt && opt.buttons){
10774                         if(opt.buttons.ok){
10775                             handleButton("ok");
10776                         }else if(opt.buttons.yes){
10777                             handleButton("yes");
10778                         }
10779                     }
10780                 });
10781                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
10782                 textareaEl.enableDisplayMode();
10783                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
10784                 progressEl.enableDisplayMode();
10785                 var pf = progressEl.dom.firstChild;
10786                 if (pf) {
10787                     pp = Roo.get(pf.firstChild);
10788                     pp.setHeight(pf.offsetHeight);
10789                 }
10790                 
10791             }
10792             return dlg;
10793         },
10794
10795         /**
10796          * Updates the message box body text
10797          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
10798          * the XHTML-compliant non-breaking space character '&amp;#160;')
10799          * @return {Roo.MessageBox} This message box
10800          */
10801         updateText : function(text){
10802             if(!dlg.isVisible() && !opt.width){
10803                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
10804             }
10805             msgEl.innerHTML = text || '&#160;';
10806       
10807             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
10808             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
10809             var w = Math.max(
10810                     Math.min(opt.width || cw , this.maxWidth), 
10811                     Math.max(opt.minWidth || this.minWidth, bwidth)
10812             );
10813             if(opt.prompt){
10814                 activeTextEl.setWidth(w);
10815             }
10816             if(dlg.isVisible()){
10817                 dlg.fixedcenter = false;
10818             }
10819             // to big, make it scroll. = But as usual stupid IE does not support
10820             // !important..
10821             
10822             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
10823                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
10824                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
10825             } else {
10826                 bodyEl.dom.style.height = '';
10827                 bodyEl.dom.style.overflowY = '';
10828             }
10829             if (cw > w) {
10830                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
10831             } else {
10832                 bodyEl.dom.style.overflowX = '';
10833             }
10834             
10835             dlg.setContentSize(w, bodyEl.getHeight());
10836             if(dlg.isVisible()){
10837                 dlg.fixedcenter = true;
10838             }
10839             return this;
10840         },
10841
10842         /**
10843          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
10844          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
10845          * @param {Number} value Any number between 0 and 1 (e.g., .5)
10846          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
10847          * @return {Roo.MessageBox} This message box
10848          */
10849         updateProgress : function(value, text){
10850             if(text){
10851                 this.updateText(text);
10852             }
10853             if (pp) { // weird bug on my firefox - for some reason this is not defined
10854                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
10855             }
10856             return this;
10857         },        
10858
10859         /**
10860          * Returns true if the message box is currently displayed
10861          * @return {Boolean} True if the message box is visible, else false
10862          */
10863         isVisible : function(){
10864             return dlg && dlg.isVisible();  
10865         },
10866
10867         /**
10868          * Hides the message box if it is displayed
10869          */
10870         hide : function(){
10871             if(this.isVisible()){
10872                 dlg.hide();
10873             }  
10874         },
10875
10876         /**
10877          * Displays a new message box, or reinitializes an existing message box, based on the config options
10878          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
10879          * The following config object properties are supported:
10880          * <pre>
10881 Property    Type             Description
10882 ----------  ---------------  ------------------------------------------------------------------------------------
10883 animEl            String/Element   An id or Element from which the message box should animate as it opens and
10884                                    closes (defaults to undefined)
10885 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
10886                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
10887 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
10888                                    progress and wait dialogs will ignore this property and always hide the
10889                                    close button as they can only be closed programmatically.
10890 cls               String           A custom CSS class to apply to the message box element
10891 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
10892                                    displayed (defaults to 75)
10893 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
10894                                    function will be btn (the name of the button that was clicked, if applicable,
10895                                    e.g. "ok"), and text (the value of the active text field, if applicable).
10896                                    Progress and wait dialogs will ignore this option since they do not respond to
10897                                    user actions and can only be closed programmatically, so any required function
10898                                    should be called by the same code after it closes the dialog.
10899 icon              String           A CSS class that provides a background image to be used as an icon for
10900                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
10901 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
10902 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
10903 modal             Boolean          False to allow user interaction with the page while the message box is
10904                                    displayed (defaults to true)
10905 msg               String           A string that will replace the existing message box body text (defaults
10906                                    to the XHTML-compliant non-breaking space character '&#160;')
10907 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
10908 progress          Boolean          True to display a progress bar (defaults to false)
10909 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
10910 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
10911 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
10912 title             String           The title text
10913 value             String           The string value to set into the active textbox element if displayed
10914 wait              Boolean          True to display a progress bar (defaults to false)
10915 width             Number           The width of the dialog in pixels
10916 </pre>
10917          *
10918          * Example usage:
10919          * <pre><code>
10920 Roo.Msg.show({
10921    title: 'Address',
10922    msg: 'Please enter your address:',
10923    width: 300,
10924    buttons: Roo.MessageBox.OKCANCEL,
10925    multiline: true,
10926    fn: saveAddress,
10927    animEl: 'addAddressBtn'
10928 });
10929 </code></pre>
10930          * @param {Object} config Configuration options
10931          * @return {Roo.MessageBox} This message box
10932          */
10933         show : function(options)
10934         {
10935             
10936             // this causes nightmares if you show one dialog after another
10937             // especially on callbacks..
10938              
10939             if(this.isVisible()){
10940                 
10941                 this.hide();
10942                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
10943                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
10944                 Roo.log("New Dialog Message:" +  options.msg )
10945                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
10946                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
10947                 
10948             }
10949             var d = this.getDialog();
10950             opt = options;
10951             d.setTitle(opt.title || "&#160;");
10952             d.close.setDisplayed(opt.closable !== false);
10953             activeTextEl = textboxEl;
10954             opt.prompt = opt.prompt || (opt.multiline ? true : false);
10955             if(opt.prompt){
10956                 if(opt.multiline){
10957                     textboxEl.hide();
10958                     textareaEl.show();
10959                     textareaEl.setHeight(typeof opt.multiline == "number" ?
10960                         opt.multiline : this.defaultTextHeight);
10961                     activeTextEl = textareaEl;
10962                 }else{
10963                     textboxEl.show();
10964                     textareaEl.hide();
10965                 }
10966             }else{
10967                 textboxEl.hide();
10968                 textareaEl.hide();
10969             }
10970             progressEl.setDisplayed(opt.progress === true);
10971             this.updateProgress(0);
10972             activeTextEl.dom.value = opt.value || "";
10973             if(opt.prompt){
10974                 dlg.setDefaultButton(activeTextEl);
10975             }else{
10976                 var bs = opt.buttons;
10977                 var db = null;
10978                 if(bs && bs.ok){
10979                     db = buttons["ok"];
10980                 }else if(bs && bs.yes){
10981                     db = buttons["yes"];
10982                 }
10983                 dlg.setDefaultButton(db);
10984             }
10985             bwidth = updateButtons(opt.buttons);
10986             this.updateText(opt.msg);
10987             if(opt.cls){
10988                 d.el.addClass(opt.cls);
10989             }
10990             d.proxyDrag = opt.proxyDrag === true;
10991             d.modal = opt.modal !== false;
10992             d.mask = opt.modal !== false ? mask : false;
10993             if(!d.isVisible()){
10994                 // force it to the end of the z-index stack so it gets a cursor in FF
10995                 document.body.appendChild(dlg.el.dom);
10996                 d.animateTarget = null;
10997                 d.show(options.animEl);
10998             }
10999             return this;
11000         },
11001
11002         /**
11003          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
11004          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
11005          * and closing the message box when the process is complete.
11006          * @param {String} title The title bar text
11007          * @param {String} msg The message box body text
11008          * @return {Roo.MessageBox} This message box
11009          */
11010         progress : function(title, msg){
11011             this.show({
11012                 title : title,
11013                 msg : msg,
11014                 buttons: false,
11015                 progress:true,
11016                 closable:false,
11017                 minWidth: this.minProgressWidth,
11018                 modal : true
11019             });
11020             return this;
11021         },
11022
11023         /**
11024          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
11025          * If a callback function is passed it will be called after the user clicks the button, and the
11026          * id of the button that was clicked will be passed as the only parameter to the callback
11027          * (could also be the top-right close button).
11028          * @param {String} title The title bar text
11029          * @param {String} msg The message box body text
11030          * @param {Function} fn (optional) The callback function invoked after the message box is closed
11031          * @param {Object} scope (optional) The scope of the callback function
11032          * @return {Roo.MessageBox} This message box
11033          */
11034         alert : function(title, msg, fn, scope){
11035             this.show({
11036                 title : title,
11037                 msg : msg,
11038                 buttons: this.OK,
11039                 fn: fn,
11040                 scope : scope,
11041                 modal : true
11042             });
11043             return this;
11044         },
11045
11046         /**
11047          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
11048          * interaction while waiting for a long-running process to complete that does not have defined intervals.
11049          * You are responsible for closing the message box when the process is complete.
11050          * @param {String} msg The message box body text
11051          * @param {String} title (optional) The title bar text
11052          * @return {Roo.MessageBox} This message box
11053          */
11054         wait : function(msg, title){
11055             this.show({
11056                 title : title,
11057                 msg : msg,
11058                 buttons: false,
11059                 closable:false,
11060                 progress:true,
11061                 modal:true,
11062                 width:300,
11063                 wait:true
11064             });
11065             waitTimer = Roo.TaskMgr.start({
11066                 run: function(i){
11067                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
11068                 },
11069                 interval: 1000
11070             });
11071             return this;
11072         },
11073
11074         /**
11075          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
11076          * If a callback function is passed it will be called after the user clicks either button, and the id of the
11077          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
11078          * @param {String} title The title bar text
11079          * @param {String} msg The message box body text
11080          * @param {Function} fn (optional) The callback function invoked after the message box is closed
11081          * @param {Object} scope (optional) The scope of the callback function
11082          * @return {Roo.MessageBox} This message box
11083          */
11084         confirm : function(title, msg, fn, scope){
11085             this.show({
11086                 title : title,
11087                 msg : msg,
11088                 buttons: this.YESNO,
11089                 fn: fn,
11090                 scope : scope,
11091                 modal : true
11092             });
11093             return this;
11094         },
11095
11096         /**
11097          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
11098          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
11099          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
11100          * (could also be the top-right close button) and the text that was entered will be passed as the two
11101          * parameters to the callback.
11102          * @param {String} title The title bar text
11103          * @param {String} msg The message box body text
11104          * @param {Function} fn (optional) The callback function invoked after the message box is closed
11105          * @param {Object} scope (optional) The scope of the callback function
11106          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
11107          * property, or the height in pixels to create the textbox (defaults to false / single-line)
11108          * @return {Roo.MessageBox} This message box
11109          */
11110         prompt : function(title, msg, fn, scope, multiline){
11111             this.show({
11112                 title : title,
11113                 msg : msg,
11114                 buttons: this.OKCANCEL,
11115                 fn: fn,
11116                 minWidth:250,
11117                 scope : scope,
11118                 prompt:true,
11119                 multiline: multiline,
11120                 modal : true
11121             });
11122             return this;
11123         },
11124
11125         /**
11126          * Button config that displays a single OK button
11127          * @type Object
11128          */
11129         OK : {ok:true},
11130         /**
11131          * Button config that displays Yes and No buttons
11132          * @type Object
11133          */
11134         YESNO : {yes:true, no:true},
11135         /**
11136          * Button config that displays OK and Cancel buttons
11137          * @type Object
11138          */
11139         OKCANCEL : {ok:true, cancel:true},
11140         /**
11141          * Button config that displays Yes, No and Cancel buttons
11142          * @type Object
11143          */
11144         YESNOCANCEL : {yes:true, no:true, cancel:true},
11145
11146         /**
11147          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
11148          * @type Number
11149          */
11150         defaultTextHeight : 75,
11151         /**
11152          * The maximum width in pixels of the message box (defaults to 600)
11153          * @type Number
11154          */
11155         maxWidth : 600,
11156         /**
11157          * The minimum width in pixels of the message box (defaults to 100)
11158          * @type Number
11159          */
11160         minWidth : 100,
11161         /**
11162          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
11163          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
11164          * @type Number
11165          */
11166         minProgressWidth : 250,
11167         /**
11168          * An object containing the default button text strings that can be overriden for localized language support.
11169          * Supported properties are: ok, cancel, yes and no.
11170          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
11171          * @type Object
11172          */
11173         buttonText : {
11174             ok : "OK",
11175             cancel : "Cancel",
11176             yes : "Yes",
11177             no : "No"
11178         }
11179     };
11180 }();
11181
11182 /**
11183  * Shorthand for {@link Roo.MessageBox}
11184  */
11185 Roo.Msg = Roo.MessageBox;/*
11186  * Based on:
11187  * Ext JS Library 1.1.1
11188  * Copyright(c) 2006-2007, Ext JS, LLC.
11189  *
11190  * Originally Released Under LGPL - original licence link has changed is not relivant.
11191  *
11192  * Fork - LGPL
11193  * <script type="text/javascript">
11194  */
11195 /**
11196  * @class Roo.QuickTips
11197  * Provides attractive and customizable tooltips for any element.
11198  * @singleton
11199  */
11200 Roo.QuickTips = function(){
11201     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
11202     var ce, bd, xy, dd;
11203     var visible = false, disabled = true, inited = false;
11204     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
11205     
11206     var onOver = function(e){
11207         if(disabled){
11208             return;
11209         }
11210         var t = e.getTarget();
11211         if(!t || t.nodeType !== 1 || t == document || t == document.body){
11212             return;
11213         }
11214         if(ce && t == ce.el){
11215             clearTimeout(hideProc);
11216             return;
11217         }
11218         if(t && tagEls[t.id]){
11219             tagEls[t.id].el = t;
11220             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
11221             return;
11222         }
11223         var ttp, et = Roo.fly(t);
11224         var ns = cfg.namespace;
11225         if(tm.interceptTitles && t.title){
11226             ttp = t.title;
11227             t.qtip = ttp;
11228             t.removeAttribute("title");
11229             e.preventDefault();
11230         }else{
11231             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
11232         }
11233         if(ttp){
11234             showProc = show.defer(tm.showDelay, tm, [{
11235                 el: t, 
11236                 text: ttp.replace(/\\n/g,'<br/>'),
11237                 width: et.getAttributeNS(ns, cfg.width),
11238                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
11239                 title: et.getAttributeNS(ns, cfg.title),
11240                     cls: et.getAttributeNS(ns, cfg.cls)
11241             }]);
11242         }
11243     };
11244     
11245     var onOut = function(e){
11246         clearTimeout(showProc);
11247         var t = e.getTarget();
11248         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
11249             hideProc = setTimeout(hide, tm.hideDelay);
11250         }
11251     };
11252     
11253     var onMove = function(e){
11254         if(disabled){
11255             return;
11256         }
11257         xy = e.getXY();
11258         xy[1] += 18;
11259         if(tm.trackMouse && ce){
11260             el.setXY(xy);
11261         }
11262     };
11263     
11264     var onDown = function(e){
11265         clearTimeout(showProc);
11266         clearTimeout(hideProc);
11267         if(!e.within(el)){
11268             if(tm.hideOnClick){
11269                 hide();
11270                 tm.disable();
11271                 tm.enable.defer(100, tm);
11272             }
11273         }
11274     };
11275     
11276     var getPad = function(){
11277         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
11278     };
11279
11280     var show = function(o){
11281         if(disabled){
11282             return;
11283         }
11284         clearTimeout(dismissProc);
11285         ce = o;
11286         if(removeCls){ // in case manually hidden
11287             el.removeClass(removeCls);
11288             removeCls = null;
11289         }
11290         if(ce.cls){
11291             el.addClass(ce.cls);
11292             removeCls = ce.cls;
11293         }
11294         if(ce.title){
11295             tipTitle.update(ce.title);
11296             tipTitle.show();
11297         }else{
11298             tipTitle.update('');
11299             tipTitle.hide();
11300         }
11301         el.dom.style.width  = tm.maxWidth+'px';
11302         //tipBody.dom.style.width = '';
11303         tipBodyText.update(o.text);
11304         var p = getPad(), w = ce.width;
11305         if(!w){
11306             var td = tipBodyText.dom;
11307             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
11308             if(aw > tm.maxWidth){
11309                 w = tm.maxWidth;
11310             }else if(aw < tm.minWidth){
11311                 w = tm.minWidth;
11312             }else{
11313                 w = aw;
11314             }
11315         }
11316         //tipBody.setWidth(w);
11317         el.setWidth(parseInt(w, 10) + p);
11318         if(ce.autoHide === false){
11319             close.setDisplayed(true);
11320             if(dd){
11321                 dd.unlock();
11322             }
11323         }else{
11324             close.setDisplayed(false);
11325             if(dd){
11326                 dd.lock();
11327             }
11328         }
11329         if(xy){
11330             el.avoidY = xy[1]-18;
11331             el.setXY(xy);
11332         }
11333         if(tm.animate){
11334             el.setOpacity(.1);
11335             el.setStyle("visibility", "visible");
11336             el.fadeIn({callback: afterShow});
11337         }else{
11338             afterShow();
11339         }
11340     };
11341     
11342     var afterShow = function(){
11343         if(ce){
11344             el.show();
11345             esc.enable();
11346             if(tm.autoDismiss && ce.autoHide !== false){
11347                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
11348             }
11349         }
11350     };
11351     
11352     var hide = function(noanim){
11353         clearTimeout(dismissProc);
11354         clearTimeout(hideProc);
11355         ce = null;
11356         if(el.isVisible()){
11357             esc.disable();
11358             if(noanim !== true && tm.animate){
11359                 el.fadeOut({callback: afterHide});
11360             }else{
11361                 afterHide();
11362             } 
11363         }
11364     };
11365     
11366     var afterHide = function(){
11367         el.hide();
11368         if(removeCls){
11369             el.removeClass(removeCls);
11370             removeCls = null;
11371         }
11372     };
11373     
11374     return {
11375         /**
11376         * @cfg {Number} minWidth
11377         * The minimum width of the quick tip (defaults to 40)
11378         */
11379        minWidth : 40,
11380         /**
11381         * @cfg {Number} maxWidth
11382         * The maximum width of the quick tip (defaults to 300)
11383         */
11384        maxWidth : 300,
11385         /**
11386         * @cfg {Boolean} interceptTitles
11387         * True to automatically use the element's DOM title value if available (defaults to false)
11388         */
11389        interceptTitles : false,
11390         /**
11391         * @cfg {Boolean} trackMouse
11392         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
11393         */
11394        trackMouse : false,
11395         /**
11396         * @cfg {Boolean} hideOnClick
11397         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
11398         */
11399        hideOnClick : true,
11400         /**
11401         * @cfg {Number} showDelay
11402         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
11403         */
11404        showDelay : 500,
11405         /**
11406         * @cfg {Number} hideDelay
11407         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
11408         */
11409        hideDelay : 200,
11410         /**
11411         * @cfg {Boolean} autoHide
11412         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
11413         * Used in conjunction with hideDelay.
11414         */
11415        autoHide : true,
11416         /**
11417         * @cfg {Boolean}
11418         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
11419         * (defaults to true).  Used in conjunction with autoDismissDelay.
11420         */
11421        autoDismiss : true,
11422         /**
11423         * @cfg {Number}
11424         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
11425         */
11426        autoDismissDelay : 5000,
11427        /**
11428         * @cfg {Boolean} animate
11429         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
11430         */
11431        animate : false,
11432
11433        /**
11434         * @cfg {String} title
11435         * Title text to display (defaults to '').  This can be any valid HTML markup.
11436         */
11437         title: '',
11438        /**
11439         * @cfg {String} text
11440         * Body text to display (defaults to '').  This can be any valid HTML markup.
11441         */
11442         text : '',
11443        /**
11444         * @cfg {String} cls
11445         * A CSS class to apply to the base quick tip element (defaults to '').
11446         */
11447         cls : '',
11448        /**
11449         * @cfg {Number} width
11450         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
11451         * minWidth or maxWidth.
11452         */
11453         width : null,
11454
11455     /**
11456      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
11457      * or display QuickTips in a page.
11458      */
11459        init : function(){
11460           tm = Roo.QuickTips;
11461           cfg = tm.tagConfig;
11462           if(!inited){
11463               if(!Roo.isReady){ // allow calling of init() before onReady
11464                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
11465                   return;
11466               }
11467               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
11468               el.fxDefaults = {stopFx: true};
11469               // maximum custom styling
11470               //el.update('<div class="x-tip-top-left"><div class="x-tip-top-right"><div class="x-tip-top"></div></div></div><div class="x-tip-bd-left"><div class="x-tip-bd-right"><div class="x-tip-bd"><div class="x-tip-close"></div><h3></h3><div class="x-tip-bd-inner"></div><div class="x-clear"></div></div></div></div><div class="x-tip-ft-left"><div class="x-tip-ft-right"><div class="x-tip-ft"></div></div></div>');
11471               el.update('<div class="x-tip-bd"><div class="x-tip-close"></div><h3></h3><div class="x-tip-bd-inner"></div><div class="x-clear"></div></div>');              
11472               tipTitle = el.child('h3');
11473               tipTitle.enableDisplayMode("block");
11474               tipBody = el.child('div.x-tip-bd');
11475               tipBodyText = el.child('div.x-tip-bd-inner');
11476               //bdLeft = el.child('div.x-tip-bd-left');
11477               //bdRight = el.child('div.x-tip-bd-right');
11478               close = el.child('div.x-tip-close');
11479               close.enableDisplayMode("block");
11480               close.on("click", hide);
11481               var d = Roo.get(document);
11482               d.on("mousedown", onDown);
11483               d.on("mouseover", onOver);
11484               d.on("mouseout", onOut);
11485               d.on("mousemove", onMove);
11486               esc = d.addKeyListener(27, hide);
11487               esc.disable();
11488               if(Roo.dd.DD){
11489                   dd = el.initDD("default", null, {
11490                       onDrag : function(){
11491                           el.sync();  
11492                       }
11493                   });
11494                   dd.setHandleElId(tipTitle.id);
11495                   dd.lock();
11496               }
11497               inited = true;
11498           }
11499           this.enable(); 
11500        },
11501
11502     /**
11503      * Configures a new quick tip instance and assigns it to a target element.  The following config options
11504      * are supported:
11505      * <pre>
11506 Property    Type                   Description
11507 ----------  ---------------------  ------------------------------------------------------------------------
11508 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
11509      * </ul>
11510      * @param {Object} config The config object
11511      */
11512        register : function(config){
11513            var cs = config instanceof Array ? config : arguments;
11514            for(var i = 0, len = cs.length; i < len; i++) {
11515                var c = cs[i];
11516                var target = c.target;
11517                if(target){
11518                    if(target instanceof Array){
11519                        for(var j = 0, jlen = target.length; j < jlen; j++){
11520                            tagEls[target[j]] = c;
11521                        }
11522                    }else{
11523                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
11524                    }
11525                }
11526            }
11527        },
11528
11529     /**
11530      * Removes this quick tip from its element and destroys it.
11531      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
11532      */
11533        unregister : function(el){
11534            delete tagEls[Roo.id(el)];
11535        },
11536
11537     /**
11538      * Enable this quick tip.
11539      */
11540        enable : function(){
11541            if(inited && disabled){
11542                locks.pop();
11543                if(locks.length < 1){
11544                    disabled = false;
11545                }
11546            }
11547        },
11548
11549     /**
11550      * Disable this quick tip.
11551      */
11552        disable : function(){
11553           disabled = true;
11554           clearTimeout(showProc);
11555           clearTimeout(hideProc);
11556           clearTimeout(dismissProc);
11557           if(ce){
11558               hide(true);
11559           }
11560           locks.push(1);
11561        },
11562
11563     /**
11564      * Returns true if the quick tip is enabled, else false.
11565      */
11566        isEnabled : function(){
11567             return !disabled;
11568        },
11569
11570         // private
11571        tagConfig : {
11572            namespace : "roo", // was ext?? this may break..
11573            alt_namespace : "ext",
11574            attribute : "qtip",
11575            width : "width",
11576            target : "target",
11577            title : "qtitle",
11578            hide : "hide",
11579            cls : "qclass"
11580        }
11581    };
11582 }();
11583
11584 // backwards compat
11585 Roo.QuickTips.tips = Roo.QuickTips.register;/*
11586  * Based on:
11587  * Ext JS Library 1.1.1
11588  * Copyright(c) 2006-2007, Ext JS, LLC.
11589  *
11590  * Originally Released Under LGPL - original licence link has changed is not relivant.
11591  *
11592  * Fork - LGPL
11593  * <script type="text/javascript">
11594  */
11595  
11596
11597 /**
11598  * @class Roo.tree.TreePanel
11599  * @extends Roo.data.Tree
11600
11601  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
11602  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
11603  * @cfg {Boolean} enableDD true to enable drag and drop
11604  * @cfg {Boolean} enableDrag true to enable just drag
11605  * @cfg {Boolean} enableDrop true to enable just drop
11606  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
11607  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
11608  * @cfg {String} ddGroup The DD group this TreePanel belongs to
11609  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
11610  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
11611  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
11612  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
11613  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
11614  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
11615  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
11616  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
11617  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
11618  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
11619  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
11620  * @cfg {Function} renderer DEPRECATED - use TreeLoader:create event / Sets the rendering (formatting) function for the nodes. to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
11621  * @cfg {Function} rendererTip DEPRECATED - use TreeLoader:create event / Sets the rendering (formatting) function for the nodes hovertip to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
11622  * 
11623  * @constructor
11624  * @param {String/HTMLElement/Element} el The container element
11625  * @param {Object} config
11626  */
11627 Roo.tree.TreePanel = function(el, config){
11628     var root = false;
11629     var loader = false;
11630     if (config.root) {
11631         root = config.root;
11632         delete config.root;
11633     }
11634     if (config.loader) {
11635         loader = config.loader;
11636         delete config.loader;
11637     }
11638     
11639     Roo.apply(this, config);
11640     Roo.tree.TreePanel.superclass.constructor.call(this);
11641     this.el = Roo.get(el);
11642     this.el.addClass('x-tree');
11643     //console.log(root);
11644     if (root) {
11645         this.setRootNode( Roo.factory(root, Roo.tree));
11646     }
11647     if (loader) {
11648         this.loader = Roo.factory(loader, Roo.tree);
11649     }
11650    /**
11651     * Read-only. The id of the container element becomes this TreePanel's id.
11652     */
11653     this.id = this.el.id;
11654     this.addEvents({
11655         /**
11656         * @event beforeload
11657         * Fires before a node is loaded, return false to cancel
11658         * @param {Node} node The node being loaded
11659         */
11660         "beforeload" : true,
11661         /**
11662         * @event load
11663         * Fires when a node is loaded
11664         * @param {Node} node The node that was loaded
11665         */
11666         "load" : true,
11667         /**
11668         * @event textchange
11669         * Fires when the text for a node is changed
11670         * @param {Node} node The node
11671         * @param {String} text The new text
11672         * @param {String} oldText The old text
11673         */
11674         "textchange" : true,
11675         /**
11676         * @event beforeexpand
11677         * Fires before a node is expanded, return false to cancel.
11678         * @param {Node} node The node
11679         * @param {Boolean} deep
11680         * @param {Boolean} anim
11681         */
11682         "beforeexpand" : true,
11683         /**
11684         * @event beforecollapse
11685         * Fires before a node is collapsed, return false to cancel.
11686         * @param {Node} node The node
11687         * @param {Boolean} deep
11688         * @param {Boolean} anim
11689         */
11690         "beforecollapse" : true,
11691         /**
11692         * @event expand
11693         * Fires when a node is expanded
11694         * @param {Node} node The node
11695         */
11696         "expand" : true,
11697         /**
11698         * @event disabledchange
11699         * Fires when the disabled status of a node changes
11700         * @param {Node} node The node
11701         * @param {Boolean} disabled
11702         */
11703         "disabledchange" : true,
11704         /**
11705         * @event collapse
11706         * Fires when a node is collapsed
11707         * @param {Node} node The node
11708         */
11709         "collapse" : true,
11710         /**
11711         * @event beforeclick
11712         * Fires before click processing on a node. Return false to cancel the default action.
11713         * @param {Node} node The node
11714         * @param {Roo.EventObject} e The event object
11715         */
11716         "beforeclick":true,
11717         /**
11718         * @event checkchange
11719         * Fires when a node with a checkbox's checked property changes
11720         * @param {Node} this This node
11721         * @param {Boolean} checked
11722         */
11723         "checkchange":true,
11724         /**
11725         * @event click
11726         * Fires when a node is clicked
11727         * @param {Node} node The node
11728         * @param {Roo.EventObject} e The event object
11729         */
11730         "click":true,
11731         /**
11732         * @event dblclick
11733         * Fires when a node is double clicked
11734         * @param {Node} node The node
11735         * @param {Roo.EventObject} e The event object
11736         */
11737         "dblclick":true,
11738         /**
11739         * @event contextmenu
11740         * Fires when a node is right clicked
11741         * @param {Node} node The node
11742         * @param {Roo.EventObject} e The event object
11743         */
11744         "contextmenu":true,
11745         /**
11746         * @event beforechildrenrendered
11747         * Fires right before the child nodes for a node are rendered
11748         * @param {Node} node The node
11749         */
11750         "beforechildrenrendered":true,
11751         /**
11752         * @event startdrag
11753         * Fires when a node starts being dragged
11754         * @param {Roo.tree.TreePanel} this
11755         * @param {Roo.tree.TreeNode} node
11756         * @param {event} e The raw browser event
11757         */ 
11758        "startdrag" : true,
11759        /**
11760         * @event enddrag
11761         * Fires when a drag operation is complete
11762         * @param {Roo.tree.TreePanel} this
11763         * @param {Roo.tree.TreeNode} node
11764         * @param {event} e The raw browser event
11765         */
11766        "enddrag" : true,
11767        /**
11768         * @event dragdrop
11769         * Fires when a dragged node is dropped on a valid DD target
11770         * @param {Roo.tree.TreePanel} this
11771         * @param {Roo.tree.TreeNode} node
11772         * @param {DD} dd The dd it was dropped on
11773         * @param {event} e The raw browser event
11774         */
11775        "dragdrop" : true,
11776        /**
11777         * @event beforenodedrop
11778         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
11779         * passed to handlers has the following properties:<br />
11780         * <ul style="padding:5px;padding-left:16px;">
11781         * <li>tree - The TreePanel</li>
11782         * <li>target - The node being targeted for the drop</li>
11783         * <li>data - The drag data from the drag source</li>
11784         * <li>point - The point of the drop - append, above or below</li>
11785         * <li>source - The drag source</li>
11786         * <li>rawEvent - Raw mouse event</li>
11787         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
11788         * to be inserted by setting them on this object.</li>
11789         * <li>cancel - Set this to true to cancel the drop.</li>
11790         * </ul>
11791         * @param {Object} dropEvent
11792         */
11793        "beforenodedrop" : true,
11794        /**
11795         * @event nodedrop
11796         * Fires after a DD object is dropped on a node in this tree. The dropEvent
11797         * passed to handlers has the following properties:<br />
11798         * <ul style="padding:5px;padding-left:16px;">
11799         * <li>tree - The TreePanel</li>
11800         * <li>target - The node being targeted for the drop</li>
11801         * <li>data - The drag data from the drag source</li>
11802         * <li>point - The point of the drop - append, above or below</li>
11803         * <li>source - The drag source</li>
11804         * <li>rawEvent - Raw mouse event</li>
11805         * <li>dropNode - Dropped node(s).</li>
11806         * </ul>
11807         * @param {Object} dropEvent
11808         */
11809        "nodedrop" : true,
11810         /**
11811         * @event nodedragover
11812         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
11813         * passed to handlers has the following properties:<br />
11814         * <ul style="padding:5px;padding-left:16px;">
11815         * <li>tree - The TreePanel</li>
11816         * <li>target - The node being targeted for the drop</li>
11817         * <li>data - The drag data from the drag source</li>
11818         * <li>point - The point of the drop - append, above or below</li>
11819         * <li>source - The drag source</li>
11820         * <li>rawEvent - Raw mouse event</li>
11821         * <li>dropNode - Drop node(s) provided by the source.</li>
11822         * <li>cancel - Set this to true to signal drop not allowed.</li>
11823         * </ul>
11824         * @param {Object} dragOverEvent
11825         */
11826        "nodedragover" : true
11827         
11828     });
11829     if(this.singleExpand){
11830        this.on("beforeexpand", this.restrictExpand, this);
11831     }
11832     if (this.editor) {
11833         this.editor.tree = this;
11834         this.editor = Roo.factory(this.editor, Roo.tree);
11835     }
11836     
11837     if (this.selModel) {
11838         this.selModel = Roo.factory(this.selModel, Roo.tree);
11839     }
11840    
11841 };
11842 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
11843     rootVisible : true,
11844     animate: Roo.enableFx,
11845     lines : true,
11846     enableDD : false,
11847     hlDrop : Roo.enableFx,
11848   
11849     renderer: false,
11850     
11851     rendererTip: false,
11852     // private
11853     restrictExpand : function(node){
11854         var p = node.parentNode;
11855         if(p){
11856             if(p.expandedChild && p.expandedChild.parentNode == p){
11857                 p.expandedChild.collapse();
11858             }
11859             p.expandedChild = node;
11860         }
11861     },
11862
11863     // private override
11864     setRootNode : function(node){
11865         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
11866         if(!this.rootVisible){
11867             node.ui = new Roo.tree.RootTreeNodeUI(node);
11868         }
11869         return node;
11870     },
11871
11872     /**
11873      * Returns the container element for this TreePanel
11874      */
11875     getEl : function(){
11876         return this.el;
11877     },
11878
11879     /**
11880      * Returns the default TreeLoader for this TreePanel
11881      */
11882     getLoader : function(){
11883         return this.loader;
11884     },
11885
11886     /**
11887      * Expand all nodes
11888      */
11889     expandAll : function(){
11890         this.root.expand(true);
11891     },
11892
11893     /**
11894      * Collapse all nodes
11895      */
11896     collapseAll : function(){
11897         this.root.collapse(true);
11898     },
11899
11900     /**
11901      * Returns the selection model used by this TreePanel
11902      */
11903     getSelectionModel : function(){
11904         if(!this.selModel){
11905             this.selModel = new Roo.tree.DefaultSelectionModel();
11906         }
11907         return this.selModel;
11908     },
11909
11910     /**
11911      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
11912      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
11913      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
11914      * @return {Array}
11915      */
11916     getChecked : function(a, startNode){
11917         startNode = startNode || this.root;
11918         var r = [];
11919         var f = function(){
11920             if(this.attributes.checked){
11921                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
11922             }
11923         }
11924         startNode.cascade(f);
11925         return r;
11926     },
11927
11928     /**
11929      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11930      * @param {String} path
11931      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11932      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
11933      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
11934      */
11935     expandPath : function(path, attr, callback){
11936         attr = attr || "id";
11937         var keys = path.split(this.pathSeparator);
11938         var curNode = this.root;
11939         if(curNode.attributes[attr] != keys[1]){ // invalid root
11940             if(callback){
11941                 callback(false, null);
11942             }
11943             return;
11944         }
11945         var index = 1;
11946         var f = function(){
11947             if(++index == keys.length){
11948                 if(callback){
11949                     callback(true, curNode);
11950                 }
11951                 return;
11952             }
11953             var c = curNode.findChild(attr, keys[index]);
11954             if(!c){
11955                 if(callback){
11956                     callback(false, curNode);
11957                 }
11958                 return;
11959             }
11960             curNode = c;
11961             c.expand(false, false, f);
11962         };
11963         curNode.expand(false, false, f);
11964     },
11965
11966     /**
11967      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11968      * @param {String} path
11969      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11970      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
11971      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
11972      */
11973     selectPath : function(path, attr, callback){
11974         attr = attr || "id";
11975         var keys = path.split(this.pathSeparator);
11976         var v = keys.pop();
11977         if(keys.length > 0){
11978             var f = function(success, node){
11979                 if(success && node){
11980                     var n = node.findChild(attr, v);
11981                     if(n){
11982                         n.select();
11983                         if(callback){
11984                             callback(true, n);
11985                         }
11986                     }else if(callback){
11987                         callback(false, n);
11988                     }
11989                 }else{
11990                     if(callback){
11991                         callback(false, n);
11992                     }
11993                 }
11994             };
11995             this.expandPath(keys.join(this.pathSeparator), attr, f);
11996         }else{
11997             this.root.select();
11998             if(callback){
11999                 callback(true, this.root);
12000             }
12001         }
12002     },
12003
12004     getTreeEl : function(){
12005         return this.el;
12006     },
12007
12008     /**
12009      * Trigger rendering of this TreePanel
12010      */
12011     render : function(){
12012         if (this.innerCt) {
12013             return this; // stop it rendering more than once!!
12014         }
12015         
12016         this.innerCt = this.el.createChild({tag:"ul",
12017                cls:"x-tree-root-ct " +
12018                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
12019
12020         if(this.containerScroll){
12021             Roo.dd.ScrollManager.register(this.el);
12022         }
12023         if((this.enableDD || this.enableDrop) && !this.dropZone){
12024            /**
12025             * The dropZone used by this tree if drop is enabled
12026             * @type Roo.tree.TreeDropZone
12027             */
12028              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
12029                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
12030            });
12031         }
12032         if((this.enableDD || this.enableDrag) && !this.dragZone){
12033            /**
12034             * The dragZone used by this tree if drag is enabled
12035             * @type Roo.tree.TreeDragZone
12036             */
12037             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
12038                ddGroup: this.ddGroup || "TreeDD",
12039                scroll: this.ddScroll
12040            });
12041         }
12042         this.getSelectionModel().init(this);
12043         if (!this.root) {
12044             Roo.log("ROOT not set in tree");
12045             return this;
12046         }
12047         this.root.render();
12048         if(!this.rootVisible){
12049             this.root.renderChildren();
12050         }
12051         return this;
12052     }
12053 });/*
12054  * Based on:
12055  * Ext JS Library 1.1.1
12056  * Copyright(c) 2006-2007, Ext JS, LLC.
12057  *
12058  * Originally Released Under LGPL - original licence link has changed is not relivant.
12059  *
12060  * Fork - LGPL
12061  * <script type="text/javascript">
12062  */
12063  
12064
12065 /**
12066  * @class Roo.tree.DefaultSelectionModel
12067  * @extends Roo.util.Observable
12068  * The default single selection for a TreePanel.
12069  * @param {Object} cfg Configuration
12070  */
12071 Roo.tree.DefaultSelectionModel = function(cfg){
12072    this.selNode = null;
12073    
12074    
12075    
12076    this.addEvents({
12077        /**
12078         * @event selectionchange
12079         * Fires when the selected node changes
12080         * @param {DefaultSelectionModel} this
12081         * @param {TreeNode} node the new selection
12082         */
12083        "selectionchange" : true,
12084
12085        /**
12086         * @event beforeselect
12087         * Fires before the selected node changes, return false to cancel the change
12088         * @param {DefaultSelectionModel} this
12089         * @param {TreeNode} node the new selection
12090         * @param {TreeNode} node the old selection
12091         */
12092        "beforeselect" : true
12093    });
12094    
12095     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
12096 };
12097
12098 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
12099     init : function(tree){
12100         this.tree = tree;
12101         tree.getTreeEl().on("keydown", this.onKeyDown, this);
12102         tree.on("click", this.onNodeClick, this);
12103     },
12104     
12105     onNodeClick : function(node, e){
12106         if (e.ctrlKey && this.selNode == node)  {
12107             this.unselect(node);
12108             return;
12109         }
12110         this.select(node);
12111     },
12112     
12113     /**
12114      * Select a node.
12115      * @param {TreeNode} node The node to select
12116      * @return {TreeNode} The selected node
12117      */
12118     select : function(node){
12119         var last = this.selNode;
12120         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
12121             if(last){
12122                 last.ui.onSelectedChange(false);
12123             }
12124             this.selNode = node;
12125             node.ui.onSelectedChange(true);
12126             this.fireEvent("selectionchange", this, node, last);
12127         }
12128         return node;
12129     },
12130     
12131     /**
12132      * Deselect a node.
12133      * @param {TreeNode} node The node to unselect
12134      */
12135     unselect : function(node){
12136         if(this.selNode == node){
12137             this.clearSelections();
12138         }    
12139     },
12140     
12141     /**
12142      * Clear all selections
12143      */
12144     clearSelections : function(){
12145         var n = this.selNode;
12146         if(n){
12147             n.ui.onSelectedChange(false);
12148             this.selNode = null;
12149             this.fireEvent("selectionchange", this, null);
12150         }
12151         return n;
12152     },
12153     
12154     /**
12155      * Get the selected node
12156      * @return {TreeNode} The selected node
12157      */
12158     getSelectedNode : function(){
12159         return this.selNode;    
12160     },
12161     
12162     /**
12163      * Returns true if the node is selected
12164      * @param {TreeNode} node The node to check
12165      * @return {Boolean}
12166      */
12167     isSelected : function(node){
12168         return this.selNode == node;  
12169     },
12170
12171     /**
12172      * Selects the node above the selected node in the tree, intelligently walking the nodes
12173      * @return TreeNode The new selection
12174      */
12175     selectPrevious : function(){
12176         var s = this.selNode || this.lastSelNode;
12177         if(!s){
12178             return null;
12179         }
12180         var ps = s.previousSibling;
12181         if(ps){
12182             if(!ps.isExpanded() || ps.childNodes.length < 1){
12183                 return this.select(ps);
12184             } else{
12185                 var lc = ps.lastChild;
12186                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
12187                     lc = lc.lastChild;
12188                 }
12189                 return this.select(lc);
12190             }
12191         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
12192             return this.select(s.parentNode);
12193         }
12194         return null;
12195     },
12196
12197     /**
12198      * Selects the node above the selected node in the tree, intelligently walking the nodes
12199      * @return TreeNode The new selection
12200      */
12201     selectNext : function(){
12202         var s = this.selNode || this.lastSelNode;
12203         if(!s){
12204             return null;
12205         }
12206         if(s.firstChild && s.isExpanded()){
12207              return this.select(s.firstChild);
12208          }else if(s.nextSibling){
12209              return this.select(s.nextSibling);
12210          }else if(s.parentNode){
12211             var newS = null;
12212             s.parentNode.bubble(function(){
12213                 if(this.nextSibling){
12214                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
12215                     return false;
12216                 }
12217             });
12218             return newS;
12219          }
12220         return null;
12221     },
12222
12223     onKeyDown : function(e){
12224         var s = this.selNode || this.lastSelNode;
12225         // undesirable, but required
12226         var sm = this;
12227         if(!s){
12228             return;
12229         }
12230         var k = e.getKey();
12231         switch(k){
12232              case e.DOWN:
12233                  e.stopEvent();
12234                  this.selectNext();
12235              break;
12236              case e.UP:
12237                  e.stopEvent();
12238                  this.selectPrevious();
12239              break;
12240              case e.RIGHT:
12241                  e.preventDefault();
12242                  if(s.hasChildNodes()){
12243                      if(!s.isExpanded()){
12244                          s.expand();
12245                      }else if(s.firstChild){
12246                          this.select(s.firstChild, e);
12247                      }
12248                  }
12249              break;
12250              case e.LEFT:
12251                  e.preventDefault();
12252                  if(s.hasChildNodes() && s.isExpanded()){
12253                      s.collapse();
12254                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
12255                      this.select(s.parentNode, e);
12256                  }
12257              break;
12258         };
12259     }
12260 });
12261
12262 /**
12263  * @class Roo.tree.MultiSelectionModel
12264  * @extends Roo.util.Observable
12265  * Multi selection for a TreePanel.
12266  * @param {Object} cfg Configuration
12267  */
12268 Roo.tree.MultiSelectionModel = function(){
12269    this.selNodes = [];
12270    this.selMap = {};
12271    this.addEvents({
12272        /**
12273         * @event selectionchange
12274         * Fires when the selected nodes change
12275         * @param {MultiSelectionModel} this
12276         * @param {Array} nodes Array of the selected nodes
12277         */
12278        "selectionchange" : true
12279    });
12280    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
12281    
12282 };
12283
12284 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
12285     init : function(tree){
12286         this.tree = tree;
12287         tree.getTreeEl().on("keydown", this.onKeyDown, this);
12288         tree.on("click", this.onNodeClick, this);
12289     },
12290     
12291     onNodeClick : function(node, e){
12292         this.select(node, e, e.ctrlKey);
12293     },
12294     
12295     /**
12296      * Select a node.
12297      * @param {TreeNode} node The node to select
12298      * @param {EventObject} e (optional) An event associated with the selection
12299      * @param {Boolean} keepExisting True to retain existing selections
12300      * @return {TreeNode} The selected node
12301      */
12302     select : function(node, e, keepExisting){
12303         if(keepExisting !== true){
12304             this.clearSelections(true);
12305         }
12306         if(this.isSelected(node)){
12307             this.lastSelNode = node;
12308             return node;
12309         }
12310         this.selNodes.push(node);
12311         this.selMap[node.id] = node;
12312         this.lastSelNode = node;
12313         node.ui.onSelectedChange(true);
12314         this.fireEvent("selectionchange", this, this.selNodes);
12315         return node;
12316     },
12317     
12318     /**
12319      * Deselect a node.
12320      * @param {TreeNode} node The node to unselect
12321      */
12322     unselect : function(node){
12323         if(this.selMap[node.id]){
12324             node.ui.onSelectedChange(false);
12325             var sn = this.selNodes;
12326             var index = -1;
12327             if(sn.indexOf){
12328                 index = sn.indexOf(node);
12329             }else{
12330                 for(var i = 0, len = sn.length; i < len; i++){
12331                     if(sn[i] == node){
12332                         index = i;
12333                         break;
12334                     }
12335                 }
12336             }
12337             if(index != -1){
12338                 this.selNodes.splice(index, 1);
12339             }
12340             delete this.selMap[node.id];
12341             this.fireEvent("selectionchange", this, this.selNodes);
12342         }
12343     },
12344     
12345     /**
12346      * Clear all selections
12347      */
12348     clearSelections : function(suppressEvent){
12349         var sn = this.selNodes;
12350         if(sn.length > 0){
12351             for(var i = 0, len = sn.length; i < len; i++){
12352                 sn[i].ui.onSelectedChange(false);
12353             }
12354             this.selNodes = [];
12355             this.selMap = {};
12356             if(suppressEvent !== true){
12357                 this.fireEvent("selectionchange", this, this.selNodes);
12358             }
12359         }
12360     },
12361     
12362     /**
12363      * Returns true if the node is selected
12364      * @param {TreeNode} node The node to check
12365      * @return {Boolean}
12366      */
12367     isSelected : function(node){
12368         return this.selMap[node.id] ? true : false;  
12369     },
12370     
12371     /**
12372      * Returns an array of the selected nodes
12373      * @return {Array}
12374      */
12375     getSelectedNodes : function(){
12376         return this.selNodes;    
12377     },
12378
12379     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
12380
12381     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
12382
12383     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
12384 });/*
12385  * Based on:
12386  * Ext JS Library 1.1.1
12387  * Copyright(c) 2006-2007, Ext JS, LLC.
12388  *
12389  * Originally Released Under LGPL - original licence link has changed is not relivant.
12390  *
12391  * Fork - LGPL
12392  * <script type="text/javascript">
12393  */
12394  
12395 /**
12396  * @class Roo.tree.TreeNode
12397  * @extends Roo.data.Node
12398  * @cfg {String} text The text for this node
12399  * @cfg {Boolean} expanded true to start the node expanded
12400  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
12401  * @cfg {Boolean} allowDrop false if this node cannot be drop on
12402  * @cfg {Boolean} disabled true to start the node disabled
12403  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
12404  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
12405  * @cfg {String} cls A css class to be added to the node
12406  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
12407  * @cfg {String} href URL of the link used for the node (defaults to #)
12408  * @cfg {String} hrefTarget target frame for the link
12409  * @cfg {String} qtip An Ext QuickTip for the node
12410  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
12411  * @cfg {Boolean} singleClickExpand True for single click expand on this node
12412  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
12413  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
12414  * (defaults to undefined with no checkbox rendered)
12415  * @constructor
12416  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
12417  */
12418 Roo.tree.TreeNode = function(attributes){
12419     attributes = attributes || {};
12420     if(typeof attributes == "string"){
12421         attributes = {text: attributes};
12422     }
12423     this.childrenRendered = false;
12424     this.rendered = false;
12425     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
12426     this.expanded = attributes.expanded === true;
12427     this.isTarget = attributes.isTarget !== false;
12428     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
12429     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
12430
12431     /**
12432      * Read-only. The text for this node. To change it use setText().
12433      * @type String
12434      */
12435     this.text = attributes.text;
12436     /**
12437      * True if this node is disabled.
12438      * @type Boolean
12439      */
12440     this.disabled = attributes.disabled === true;
12441
12442     this.addEvents({
12443         /**
12444         * @event textchange
12445         * Fires when the text for this node is changed
12446         * @param {Node} this This node
12447         * @param {String} text The new text
12448         * @param {String} oldText The old text
12449         */
12450         "textchange" : true,
12451         /**
12452         * @event beforeexpand
12453         * Fires before this node is expanded, return false to cancel.
12454         * @param {Node} this This node
12455         * @param {Boolean} deep
12456         * @param {Boolean} anim
12457         */
12458         "beforeexpand" : true,
12459         /**
12460         * @event beforecollapse
12461         * Fires before this node is collapsed, return false to cancel.
12462         * @param {Node} this This node
12463         * @param {Boolean} deep
12464         * @param {Boolean} anim
12465         */
12466         "beforecollapse" : true,
12467         /**
12468         * @event expand
12469         * Fires when this node is expanded
12470         * @param {Node} this This node
12471         */
12472         "expand" : true,
12473         /**
12474         * @event disabledchange
12475         * Fires when the disabled status of this node changes
12476         * @param {Node} this This node
12477         * @param {Boolean} disabled
12478         */
12479         "disabledchange" : true,
12480         /**
12481         * @event collapse
12482         * Fires when this node is collapsed
12483         * @param {Node} this This node
12484         */
12485         "collapse" : true,
12486         /**
12487         * @event beforeclick
12488         * Fires before click processing. Return false to cancel the default action.
12489         * @param {Node} this This node
12490         * @param {Roo.EventObject} e The event object
12491         */
12492         "beforeclick":true,
12493         /**
12494         * @event checkchange
12495         * Fires when a node with a checkbox's checked property changes
12496         * @param {Node} this This node
12497         * @param {Boolean} checked
12498         */
12499         "checkchange":true,
12500         /**
12501         * @event click
12502         * Fires when this node is clicked
12503         * @param {Node} this This node
12504         * @param {Roo.EventObject} e The event object
12505         */
12506         "click":true,
12507         /**
12508         * @event dblclick
12509         * Fires when this node is double clicked
12510         * @param {Node} this This node
12511         * @param {Roo.EventObject} e The event object
12512         */
12513         "dblclick":true,
12514         /**
12515         * @event contextmenu
12516         * Fires when this node is right clicked
12517         * @param {Node} this This node
12518         * @param {Roo.EventObject} e The event object
12519         */
12520         "contextmenu":true,
12521         /**
12522         * @event beforechildrenrendered
12523         * Fires right before the child nodes for this node are rendered
12524         * @param {Node} this This node
12525         */
12526         "beforechildrenrendered":true
12527     });
12528
12529     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
12530
12531     /**
12532      * Read-only. The UI for this node
12533      * @type TreeNodeUI
12534      */
12535     this.ui = new uiClass(this);
12536     
12537     // finally support items[]
12538     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
12539         return;
12540     }
12541     
12542     
12543     Roo.each(this.attributes.items, function(c) {
12544         this.appendChild(Roo.factory(c,Roo.Tree));
12545     }, this);
12546     delete this.attributes.items;
12547     
12548     
12549     
12550 };
12551 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
12552     preventHScroll: true,
12553     /**
12554      * Returns true if this node is expanded
12555      * @return {Boolean}
12556      */
12557     isExpanded : function(){
12558         return this.expanded;
12559     },
12560
12561     /**
12562      * Returns the UI object for this node
12563      * @return {TreeNodeUI}
12564      */
12565     getUI : function(){
12566         return this.ui;
12567     },
12568
12569     // private override
12570     setFirstChild : function(node){
12571         var of = this.firstChild;
12572         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
12573         if(this.childrenRendered && of && node != of){
12574             of.renderIndent(true, true);
12575         }
12576         if(this.rendered){
12577             this.renderIndent(true, true);
12578         }
12579     },
12580
12581     // private override
12582     setLastChild : function(node){
12583         var ol = this.lastChild;
12584         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
12585         if(this.childrenRendered && ol && node != ol){
12586             ol.renderIndent(true, true);
12587         }
12588         if(this.rendered){
12589             this.renderIndent(true, true);
12590         }
12591     },
12592
12593     // these methods are overridden to provide lazy rendering support
12594     // private override
12595     appendChild : function()
12596     {
12597         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
12598         if(node && this.childrenRendered){
12599             node.render();
12600         }
12601         this.ui.updateExpandIcon();
12602         return node;
12603     },
12604
12605     // private override
12606     removeChild : function(node){
12607         this.ownerTree.getSelectionModel().unselect(node);
12608         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
12609         // if it's been rendered remove dom node
12610         if(this.childrenRendered){
12611             node.ui.remove();
12612         }
12613         if(this.childNodes.length < 1){
12614             this.collapse(false, false);
12615         }else{
12616             this.ui.updateExpandIcon();
12617         }
12618         if(!this.firstChild) {
12619             this.childrenRendered = false;
12620         }
12621         return node;
12622     },
12623
12624     // private override
12625     insertBefore : function(node, refNode){
12626         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
12627         if(newNode && refNode && this.childrenRendered){
12628             node.render();
12629         }
12630         this.ui.updateExpandIcon();
12631         return newNode;
12632     },
12633
12634     /**
12635      * Sets the text for this node
12636      * @param {String} text
12637      */
12638     setText : function(text){
12639         var oldText = this.text;
12640         this.text = text;
12641         this.attributes.text = text;
12642         if(this.rendered){ // event without subscribing
12643             this.ui.onTextChange(this, text, oldText);
12644         }
12645         this.fireEvent("textchange", this, text, oldText);
12646     },
12647
12648     /**
12649      * Triggers selection of this node
12650      */
12651     select : function(){
12652         this.getOwnerTree().getSelectionModel().select(this);
12653     },
12654
12655     /**
12656      * Triggers deselection of this node
12657      */
12658     unselect : function(){
12659         this.getOwnerTree().getSelectionModel().unselect(this);
12660     },
12661
12662     /**
12663      * Returns true if this node is selected
12664      * @return {Boolean}
12665      */
12666     isSelected : function(){
12667         return this.getOwnerTree().getSelectionModel().isSelected(this);
12668     },
12669
12670     /**
12671      * Expand this node.
12672      * @param {Boolean} deep (optional) True to expand all children as well
12673      * @param {Boolean} anim (optional) false to cancel the default animation
12674      * @param {Function} callback (optional) A callback to be called when
12675      * expanding this node completes (does not wait for deep expand to complete).
12676      * Called with 1 parameter, this node.
12677      */
12678     expand : function(deep, anim, callback){
12679         if(!this.expanded){
12680             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
12681                 return;
12682             }
12683             if(!this.childrenRendered){
12684                 this.renderChildren();
12685             }
12686             this.expanded = true;
12687             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
12688                 this.ui.animExpand(function(){
12689                     this.fireEvent("expand", this);
12690                     if(typeof callback == "function"){
12691                         callback(this);
12692                     }
12693                     if(deep === true){
12694                         this.expandChildNodes(true);
12695                     }
12696                 }.createDelegate(this));
12697                 return;
12698             }else{
12699                 this.ui.expand();
12700                 this.fireEvent("expand", this);
12701                 if(typeof callback == "function"){
12702                     callback(this);
12703                 }
12704             }
12705         }else{
12706            if(typeof callback == "function"){
12707                callback(this);
12708            }
12709         }
12710         if(deep === true){
12711             this.expandChildNodes(true);
12712         }
12713     },
12714
12715     isHiddenRoot : function(){
12716         return this.isRoot && !this.getOwnerTree().rootVisible;
12717     },
12718
12719     /**
12720      * Collapse this node.
12721      * @param {Boolean} deep (optional) True to collapse all children as well
12722      * @param {Boolean} anim (optional) false to cancel the default animation
12723      */
12724     collapse : function(deep, anim){
12725         if(this.expanded && !this.isHiddenRoot()){
12726             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
12727                 return;
12728             }
12729             this.expanded = false;
12730             if((this.getOwnerTree().animate && anim !== false) || anim){
12731                 this.ui.animCollapse(function(){
12732                     this.fireEvent("collapse", this);
12733                     if(deep === true){
12734                         this.collapseChildNodes(true);
12735                     }
12736                 }.createDelegate(this));
12737                 return;
12738             }else{
12739                 this.ui.collapse();
12740                 this.fireEvent("collapse", this);
12741             }
12742         }
12743         if(deep === true){
12744             var cs = this.childNodes;
12745             for(var i = 0, len = cs.length; i < len; i++) {
12746                 cs[i].collapse(true, false);
12747             }
12748         }
12749     },
12750
12751     // private
12752     delayedExpand : function(delay){
12753         if(!this.expandProcId){
12754             this.expandProcId = this.expand.defer(delay, this);
12755         }
12756     },
12757
12758     // private
12759     cancelExpand : function(){
12760         if(this.expandProcId){
12761             clearTimeout(this.expandProcId);
12762         }
12763         this.expandProcId = false;
12764     },
12765
12766     /**
12767      * Toggles expanded/collapsed state of the node
12768      */
12769     toggle : function(){
12770         if(this.expanded){
12771             this.collapse();
12772         }else{
12773             this.expand();
12774         }
12775     },
12776
12777     /**
12778      * Ensures all parent nodes are expanded
12779      */
12780     ensureVisible : function(callback){
12781         var tree = this.getOwnerTree();
12782         tree.expandPath(this.parentNode.getPath(), false, function(){
12783             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
12784             Roo.callback(callback);
12785         }.createDelegate(this));
12786     },
12787
12788     /**
12789      * Expand all child nodes
12790      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
12791      */
12792     expandChildNodes : function(deep){
12793         var cs = this.childNodes;
12794         for(var i = 0, len = cs.length; i < len; i++) {
12795                 cs[i].expand(deep);
12796         }
12797     },
12798
12799     /**
12800      * Collapse all child nodes
12801      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
12802      */
12803     collapseChildNodes : function(deep){
12804         var cs = this.childNodes;
12805         for(var i = 0, len = cs.length; i < len; i++) {
12806                 cs[i].collapse(deep);
12807         }
12808     },
12809
12810     /**
12811      * Disables this node
12812      */
12813     disable : function(){
12814         this.disabled = true;
12815         this.unselect();
12816         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12817             this.ui.onDisableChange(this, true);
12818         }
12819         this.fireEvent("disabledchange", this, true);
12820     },
12821
12822     /**
12823      * Enables this node
12824      */
12825     enable : function(){
12826         this.disabled = false;
12827         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12828             this.ui.onDisableChange(this, false);
12829         }
12830         this.fireEvent("disabledchange", this, false);
12831     },
12832
12833     // private
12834     renderChildren : function(suppressEvent){
12835         if(suppressEvent !== false){
12836             this.fireEvent("beforechildrenrendered", this);
12837         }
12838         var cs = this.childNodes;
12839         for(var i = 0, len = cs.length; i < len; i++){
12840             cs[i].render(true);
12841         }
12842         this.childrenRendered = true;
12843     },
12844
12845     // private
12846     sort : function(fn, scope){
12847         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
12848         if(this.childrenRendered){
12849             var cs = this.childNodes;
12850             for(var i = 0, len = cs.length; i < len; i++){
12851                 cs[i].render(true);
12852             }
12853         }
12854     },
12855
12856     // private
12857     render : function(bulkRender){
12858         this.ui.render(bulkRender);
12859         if(!this.rendered){
12860             this.rendered = true;
12861             if(this.expanded){
12862                 this.expanded = false;
12863                 this.expand(false, false);
12864             }
12865         }
12866     },
12867
12868     // private
12869     renderIndent : function(deep, refresh){
12870         if(refresh){
12871             this.ui.childIndent = null;
12872         }
12873         this.ui.renderIndent();
12874         if(deep === true && this.childrenRendered){
12875             var cs = this.childNodes;
12876             for(var i = 0, len = cs.length; i < len; i++){
12877                 cs[i].renderIndent(true, refresh);
12878             }
12879         }
12880     }
12881 });/*
12882  * Based on:
12883  * Ext JS Library 1.1.1
12884  * Copyright(c) 2006-2007, Ext JS, LLC.
12885  *
12886  * Originally Released Under LGPL - original licence link has changed is not relivant.
12887  *
12888  * Fork - LGPL
12889  * <script type="text/javascript">
12890  */
12891  
12892 /**
12893  * @class Roo.tree.AsyncTreeNode
12894  * @extends Roo.tree.TreeNode
12895  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
12896  * @constructor
12897  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
12898  */
12899  Roo.tree.AsyncTreeNode = function(config){
12900     this.loaded = false;
12901     this.loading = false;
12902     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
12903     /**
12904     * @event beforeload
12905     * Fires before this node is loaded, return false to cancel
12906     * @param {Node} this This node
12907     */
12908     this.addEvents({'beforeload':true, 'load': true});
12909     /**
12910     * @event load
12911     * Fires when this node is loaded
12912     * @param {Node} this This node
12913     */
12914     /**
12915      * The loader used by this node (defaults to using the tree's defined loader)
12916      * @type TreeLoader
12917      * @property loader
12918      */
12919 };
12920 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
12921     expand : function(deep, anim, callback){
12922         if(this.loading){ // if an async load is already running, waiting til it's done
12923             var timer;
12924             var f = function(){
12925                 if(!this.loading){ // done loading
12926                     clearInterval(timer);
12927                     this.expand(deep, anim, callback);
12928                 }
12929             }.createDelegate(this);
12930             timer = setInterval(f, 200);
12931             return;
12932         }
12933         if(!this.loaded){
12934             if(this.fireEvent("beforeload", this) === false){
12935                 return;
12936             }
12937             this.loading = true;
12938             this.ui.beforeLoad(this);
12939             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
12940             if(loader){
12941                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
12942                 return;
12943             }
12944         }
12945         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
12946     },
12947     
12948     /**
12949      * Returns true if this node is currently loading
12950      * @return {Boolean}
12951      */
12952     isLoading : function(){
12953         return this.loading;  
12954     },
12955     
12956     loadComplete : function(deep, anim, callback){
12957         this.loading = false;
12958         this.loaded = true;
12959         this.ui.afterLoad(this);
12960         this.fireEvent("load", this);
12961         this.expand(deep, anim, callback);
12962     },
12963     
12964     /**
12965      * Returns true if this node has been loaded
12966      * @return {Boolean}
12967      */
12968     isLoaded : function(){
12969         return this.loaded;
12970     },
12971     
12972     hasChildNodes : function(){
12973         if(!this.isLeaf() && !this.loaded){
12974             return true;
12975         }else{
12976             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
12977         }
12978     },
12979
12980     /**
12981      * Trigger a reload for this node
12982      * @param {Function} callback
12983      */
12984     reload : function(callback){
12985         this.collapse(false, false);
12986         while(this.firstChild){
12987             this.removeChild(this.firstChild);
12988         }
12989         this.childrenRendered = false;
12990         this.loaded = false;
12991         if(this.isHiddenRoot()){
12992             this.expanded = false;
12993         }
12994         this.expand(false, false, callback);
12995     }
12996 });/*
12997  * Based on:
12998  * Ext JS Library 1.1.1
12999  * Copyright(c) 2006-2007, Ext JS, LLC.
13000  *
13001  * Originally Released Under LGPL - original licence link has changed is not relivant.
13002  *
13003  * Fork - LGPL
13004  * <script type="text/javascript">
13005  */
13006  
13007 /**
13008  * @class Roo.tree.TreeNodeUI
13009  * @constructor
13010  * @param {Object} node The node to render
13011  * The TreeNode UI implementation is separate from the
13012  * tree implementation. Unless you are customizing the tree UI,
13013  * you should never have to use this directly.
13014  */
13015 Roo.tree.TreeNodeUI = function(node){
13016     this.node = node;
13017     this.rendered = false;
13018     this.animating = false;
13019     this.emptyIcon = Roo.BLANK_IMAGE_URL;
13020 };
13021
13022 Roo.tree.TreeNodeUI.prototype = {
13023     removeChild : function(node){
13024         if(this.rendered){
13025             this.ctNode.removeChild(node.ui.getEl());
13026         }
13027     },
13028
13029     beforeLoad : function(){
13030          this.addClass("x-tree-node-loading");
13031     },
13032
13033     afterLoad : function(){
13034          this.removeClass("x-tree-node-loading");
13035     },
13036
13037     onTextChange : function(node, text, oldText){
13038         if(this.rendered){
13039             this.textNode.innerHTML = text;
13040         }
13041     },
13042
13043     onDisableChange : function(node, state){
13044         this.disabled = state;
13045         if(state){
13046             this.addClass("x-tree-node-disabled");
13047         }else{
13048             this.removeClass("x-tree-node-disabled");
13049         }
13050     },
13051
13052     onSelectedChange : function(state){
13053         if(state){
13054             this.focus();
13055             this.addClass("x-tree-selected");
13056         }else{
13057             //this.blur();
13058             this.removeClass("x-tree-selected");
13059         }
13060     },
13061
13062     onMove : function(tree, node, oldParent, newParent, index, refNode){
13063         this.childIndent = null;
13064         if(this.rendered){
13065             var targetNode = newParent.ui.getContainer();
13066             if(!targetNode){//target not rendered
13067                 this.holder = document.createElement("div");
13068                 this.holder.appendChild(this.wrap);
13069                 return;
13070             }
13071             var insertBefore = refNode ? refNode.ui.getEl() : null;
13072             if(insertBefore){
13073                 targetNode.insertBefore(this.wrap, insertBefore);
13074             }else{
13075                 targetNode.appendChild(this.wrap);
13076             }
13077             this.node.renderIndent(true);
13078         }
13079     },
13080
13081     addClass : function(cls){
13082         if(this.elNode){
13083             Roo.fly(this.elNode).addClass(cls);
13084         }
13085     },
13086
13087     removeClass : function(cls){
13088         if(this.elNode){
13089             Roo.fly(this.elNode).removeClass(cls);
13090         }
13091     },
13092
13093     remove : function(){
13094         if(this.rendered){
13095             this.holder = document.createElement("div");
13096             this.holder.appendChild(this.wrap);
13097         }
13098     },
13099
13100     fireEvent : function(){
13101         return this.node.fireEvent.apply(this.node, arguments);
13102     },
13103
13104     initEvents : function(){
13105         this.node.on("move", this.onMove, this);
13106         var E = Roo.EventManager;
13107         var a = this.anchor;
13108
13109         var el = Roo.fly(a, '_treeui');
13110
13111         if(Roo.isOpera){ // opera render bug ignores the CSS
13112             el.setStyle("text-decoration", "none");
13113         }
13114
13115         el.on("click", this.onClick, this);
13116         el.on("dblclick", this.onDblClick, this);
13117
13118         if(this.checkbox){
13119             Roo.EventManager.on(this.checkbox,
13120                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
13121         }
13122
13123         el.on("contextmenu", this.onContextMenu, this);
13124
13125         var icon = Roo.fly(this.iconNode);
13126         icon.on("click", this.onClick, this);
13127         icon.on("dblclick", this.onDblClick, this);
13128         icon.on("contextmenu", this.onContextMenu, this);
13129         E.on(this.ecNode, "click", this.ecClick, this, true);
13130
13131         if(this.node.disabled){
13132             this.addClass("x-tree-node-disabled");
13133         }
13134         if(this.node.hidden){
13135             this.addClass("x-tree-node-disabled");
13136         }
13137         var ot = this.node.getOwnerTree();
13138         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
13139         if(dd && (!this.node.isRoot || ot.rootVisible)){
13140             Roo.dd.Registry.register(this.elNode, {
13141                 node: this.node,
13142                 handles: this.getDDHandles(),
13143                 isHandle: false
13144             });
13145         }
13146     },
13147
13148     getDDHandles : function(){
13149         return [this.iconNode, this.textNode];
13150     },
13151
13152     hide : function(){
13153         if(this.rendered){
13154             this.wrap.style.display = "none";
13155         }
13156     },
13157
13158     show : function(){
13159         if(this.rendered){
13160             this.wrap.style.display = "";
13161         }
13162     },
13163
13164     onContextMenu : function(e){
13165         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
13166             e.preventDefault();
13167             this.focus();
13168             this.fireEvent("contextmenu", this.node, e);
13169         }
13170     },
13171
13172     onClick : function(e){
13173         if(this.dropping){
13174             e.stopEvent();
13175             return;
13176         }
13177         if(this.fireEvent("beforeclick", this.node, e) !== false){
13178             if(!this.disabled && this.node.attributes.href){
13179                 this.fireEvent("click", this.node, e);
13180                 return;
13181             }
13182             e.preventDefault();
13183             if(this.disabled){
13184                 return;
13185             }
13186
13187             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
13188                 this.node.toggle();
13189             }
13190
13191             this.fireEvent("click", this.node, e);
13192         }else{
13193             e.stopEvent();
13194         }
13195     },
13196
13197     onDblClick : function(e){
13198         e.preventDefault();
13199         if(this.disabled){
13200             return;
13201         }
13202         if(this.checkbox){
13203             this.toggleCheck();
13204         }
13205         if(!this.animating && this.node.hasChildNodes()){
13206             this.node.toggle();
13207         }
13208         this.fireEvent("dblclick", this.node, e);
13209     },
13210
13211     onCheckChange : function(){
13212         var checked = this.checkbox.checked;
13213         this.node.attributes.checked = checked;
13214         this.fireEvent('checkchange', this.node, checked);
13215     },
13216
13217     ecClick : function(e){
13218         if(!this.animating && this.node.hasChildNodes()){
13219             this.node.toggle();
13220         }
13221     },
13222
13223     startDrop : function(){
13224         this.dropping = true;
13225     },
13226
13227     // delayed drop so the click event doesn't get fired on a drop
13228     endDrop : function(){
13229        setTimeout(function(){
13230            this.dropping = false;
13231        }.createDelegate(this), 50);
13232     },
13233
13234     expand : function(){
13235         this.updateExpandIcon();
13236         this.ctNode.style.display = "";
13237     },
13238
13239     focus : function(){
13240         if(!this.node.preventHScroll){
13241             try{this.anchor.focus();
13242             }catch(e){}
13243         }else if(!Roo.isIE){
13244             try{
13245                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
13246                 var l = noscroll.scrollLeft;
13247                 this.anchor.focus();
13248                 noscroll.scrollLeft = l;
13249             }catch(e){}
13250         }
13251     },
13252
13253     toggleCheck : function(value){
13254         var cb = this.checkbox;
13255         if(cb){
13256             cb.checked = (value === undefined ? !cb.checked : value);
13257         }
13258     },
13259
13260     blur : function(){
13261         try{
13262             this.anchor.blur();
13263         }catch(e){}
13264     },
13265
13266     animExpand : function(callback){
13267         var ct = Roo.get(this.ctNode);
13268         ct.stopFx();
13269         if(!this.node.hasChildNodes()){
13270             this.updateExpandIcon();
13271             this.ctNode.style.display = "";
13272             Roo.callback(callback);
13273             return;
13274         }
13275         this.animating = true;
13276         this.updateExpandIcon();
13277
13278         ct.slideIn('t', {
13279            callback : function(){
13280                this.animating = false;
13281                Roo.callback(callback);
13282             },
13283             scope: this,
13284             duration: this.node.ownerTree.duration || .25
13285         });
13286     },
13287
13288     highlight : function(){
13289         var tree = this.node.getOwnerTree();
13290         Roo.fly(this.wrap).highlight(
13291             tree.hlColor || "C3DAF9",
13292             {endColor: tree.hlBaseColor}
13293         );
13294     },
13295
13296     collapse : function(){
13297         this.updateExpandIcon();
13298         this.ctNode.style.display = "none";
13299     },
13300
13301     animCollapse : function(callback){
13302         var ct = Roo.get(this.ctNode);
13303         ct.enableDisplayMode('block');
13304         ct.stopFx();
13305
13306         this.animating = true;
13307         this.updateExpandIcon();
13308
13309         ct.slideOut('t', {
13310             callback : function(){
13311                this.animating = false;
13312                Roo.callback(callback);
13313             },
13314             scope: this,
13315             duration: this.node.ownerTree.duration || .25
13316         });
13317     },
13318
13319     getContainer : function(){
13320         return this.ctNode;
13321     },
13322
13323     getEl : function(){
13324         return this.wrap;
13325     },
13326
13327     appendDDGhost : function(ghostNode){
13328         ghostNode.appendChild(this.elNode.cloneNode(true));
13329     },
13330
13331     getDDRepairXY : function(){
13332         return Roo.lib.Dom.getXY(this.iconNode);
13333     },
13334
13335     onRender : function(){
13336         this.render();
13337     },
13338
13339     render : function(bulkRender){
13340         var n = this.node, a = n.attributes;
13341         var targetNode = n.parentNode ?
13342               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
13343
13344         if(!this.rendered){
13345             this.rendered = true;
13346
13347             this.renderElements(n, a, targetNode, bulkRender);
13348
13349             if(a.qtip){
13350                if(this.textNode.setAttributeNS){
13351                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
13352                    if(a.qtipTitle){
13353                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
13354                    }
13355                }else{
13356                    this.textNode.setAttribute("ext:qtip", a.qtip);
13357                    if(a.qtipTitle){
13358                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
13359                    }
13360                }
13361             }else if(a.qtipCfg){
13362                 a.qtipCfg.target = Roo.id(this.textNode);
13363                 Roo.QuickTips.register(a.qtipCfg);
13364             }
13365             this.initEvents();
13366             if(!this.node.expanded){
13367                 this.updateExpandIcon();
13368             }
13369         }else{
13370             if(bulkRender === true) {
13371                 targetNode.appendChild(this.wrap);
13372             }
13373         }
13374     },
13375
13376     renderElements : function(n, a, targetNode, bulkRender)
13377     {
13378         // add some indent caching, this helps performance when rendering a large tree
13379         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
13380         var t = n.getOwnerTree();
13381         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
13382         if (typeof(n.attributes.html) != 'undefined') {
13383             txt = n.attributes.html;
13384         }
13385         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
13386         var cb = typeof a.checked == 'boolean';
13387         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
13388         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
13389             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
13390             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
13391             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
13392             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
13393             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
13394              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
13395                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
13396             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
13397             "</li>"];
13398
13399         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
13400             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
13401                                 n.nextSibling.ui.getEl(), buf.join(""));
13402         }else{
13403             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
13404         }
13405
13406         this.elNode = this.wrap.childNodes[0];
13407         this.ctNode = this.wrap.childNodes[1];
13408         var cs = this.elNode.childNodes;
13409         this.indentNode = cs[0];
13410         this.ecNode = cs[1];
13411         this.iconNode = cs[2];
13412         var index = 3;
13413         if(cb){
13414             this.checkbox = cs[3];
13415             index++;
13416         }
13417         this.anchor = cs[index];
13418         this.textNode = cs[index].firstChild;
13419     },
13420
13421     getAnchor : function(){
13422         return this.anchor;
13423     },
13424
13425     getTextEl : function(){
13426         return this.textNode;
13427     },
13428
13429     getIconEl : function(){
13430         return this.iconNode;
13431     },
13432
13433     isChecked : function(){
13434         return this.checkbox ? this.checkbox.checked : false;
13435     },
13436
13437     updateExpandIcon : function(){
13438         if(this.rendered){
13439             var n = this.node, c1, c2;
13440             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
13441             var hasChild = n.hasChildNodes();
13442             if(hasChild){
13443                 if(n.expanded){
13444                     cls += "-minus";
13445                     c1 = "x-tree-node-collapsed";
13446                     c2 = "x-tree-node-expanded";
13447                 }else{
13448                     cls += "-plus";
13449                     c1 = "x-tree-node-expanded";
13450                     c2 = "x-tree-node-collapsed";
13451                 }
13452                 if(this.wasLeaf){
13453                     this.removeClass("x-tree-node-leaf");
13454                     this.wasLeaf = false;
13455                 }
13456                 if(this.c1 != c1 || this.c2 != c2){
13457                     Roo.fly(this.elNode).replaceClass(c1, c2);
13458                     this.c1 = c1; this.c2 = c2;
13459                 }
13460             }else{
13461                 // this changes non-leafs into leafs if they have no children.
13462                 // it's not very rational behaviour..
13463                 
13464                 if(!this.wasLeaf && this.node.leaf){
13465                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
13466                     delete this.c1;
13467                     delete this.c2;
13468                     this.wasLeaf = true;
13469                 }
13470             }
13471             var ecc = "x-tree-ec-icon "+cls;
13472             if(this.ecc != ecc){
13473                 this.ecNode.className = ecc;
13474                 this.ecc = ecc;
13475             }
13476         }
13477     },
13478
13479     getChildIndent : function(){
13480         if(!this.childIndent){
13481             var buf = [];
13482             var p = this.node;
13483             while(p){
13484                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
13485                     if(!p.isLast()) {
13486                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
13487                     } else {
13488                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
13489                     }
13490                 }
13491                 p = p.parentNode;
13492             }
13493             this.childIndent = buf.join("");
13494         }
13495         return this.childIndent;
13496     },
13497
13498     renderIndent : function(){
13499         if(this.rendered){
13500             var indent = "";
13501             var p = this.node.parentNode;
13502             if(p){
13503                 indent = p.ui.getChildIndent();
13504             }
13505             if(this.indentMarkup != indent){ // don't rerender if not required
13506                 this.indentNode.innerHTML = indent;
13507                 this.indentMarkup = indent;
13508             }
13509             this.updateExpandIcon();
13510         }
13511     }
13512 };
13513
13514 Roo.tree.RootTreeNodeUI = function(){
13515     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
13516 };
13517 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
13518     render : function(){
13519         if(!this.rendered){
13520             var targetNode = this.node.ownerTree.innerCt.dom;
13521             this.node.expanded = true;
13522             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
13523             this.wrap = this.ctNode = targetNode.firstChild;
13524         }
13525     },
13526     collapse : function(){
13527     },
13528     expand : function(){
13529     }
13530 });/*
13531  * Based on:
13532  * Ext JS Library 1.1.1
13533  * Copyright(c) 2006-2007, Ext JS, LLC.
13534  *
13535  * Originally Released Under LGPL - original licence link has changed is not relivant.
13536  *
13537  * Fork - LGPL
13538  * <script type="text/javascript">
13539  */
13540 /**
13541  * @class Roo.tree.TreeLoader
13542  * @extends Roo.util.Observable
13543  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
13544  * nodes from a specified URL. The response must be a javascript Array definition
13545  * who's elements are node definition objects. eg:
13546  * <pre><code>
13547 {  success : true,
13548    data :      [
13549    
13550     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
13551     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
13552     ]
13553 }
13554
13555
13556 </code></pre>
13557  * <br><br>
13558  * The old style respose with just an array is still supported, but not recommended.
13559  * <br><br>
13560  *
13561  * A server request is sent, and child nodes are loaded only when a node is expanded.
13562  * The loading node's id is passed to the server under the parameter name "node" to
13563  * enable the server to produce the correct child nodes.
13564  * <br><br>
13565  * To pass extra parameters, an event handler may be attached to the "beforeload"
13566  * event, and the parameters specified in the TreeLoader's baseParams property:
13567  * <pre><code>
13568     myTreeLoader.on("beforeload", function(treeLoader, node) {
13569         this.baseParams.category = node.attributes.category;
13570     }, this);
13571     
13572 </code></pre>
13573  *
13574  * This would pass an HTTP parameter called "category" to the server containing
13575  * the value of the Node's "category" attribute.
13576  * @constructor
13577  * Creates a new Treeloader.
13578  * @param {Object} config A config object containing config properties.
13579  */
13580 Roo.tree.TreeLoader = function(config){
13581     this.baseParams = {};
13582     this.requestMethod = "POST";
13583     Roo.apply(this, config);
13584
13585     this.addEvents({
13586     
13587         /**
13588          * @event beforeload
13589          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
13590          * @param {Object} This TreeLoader object.
13591          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13592          * @param {Object} callback The callback function specified in the {@link #load} call.
13593          */
13594         beforeload : true,
13595         /**
13596          * @event load
13597          * Fires when the node has been successfuly loaded.
13598          * @param {Object} This TreeLoader object.
13599          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13600          * @param {Object} response The response object containing the data from the server.
13601          */
13602         load : true,
13603         /**
13604          * @event loadexception
13605          * Fires if the network request failed.
13606          * @param {Object} This TreeLoader object.
13607          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13608          * @param {Object} response The response object containing the data from the server.
13609          */
13610         loadexception : true,
13611         /**
13612          * @event create
13613          * Fires before a node is created, enabling you to return custom Node types 
13614          * @param {Object} This TreeLoader object.
13615          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
13616          */
13617         create : true
13618     });
13619
13620     Roo.tree.TreeLoader.superclass.constructor.call(this);
13621 };
13622
13623 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
13624     /**
13625     * @cfg {String} dataUrl The URL from which to request a Json string which
13626     * specifies an array of node definition object representing the child nodes
13627     * to be loaded.
13628     */
13629     /**
13630     * @cfg {String} requestMethod either GET or POST
13631     * defaults to POST (due to BC)
13632     * to be loaded.
13633     */
13634     /**
13635     * @cfg {Object} baseParams (optional) An object containing properties which
13636     * specify HTTP parameters to be passed to each request for child nodes.
13637     */
13638     /**
13639     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
13640     * created by this loader. If the attributes sent by the server have an attribute in this object,
13641     * they take priority.
13642     */
13643     /**
13644     * @cfg {Object} uiProviders (optional) An object containing properties which
13645     * 
13646     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
13647     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
13648     * <i>uiProvider</i> attribute of a returned child node is a string rather
13649     * than a reference to a TreeNodeUI implementation, this that string value
13650     * is used as a property name in the uiProviders object. You can define the provider named
13651     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
13652     */
13653     uiProviders : {},
13654
13655     /**
13656     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
13657     * child nodes before loading.
13658     */
13659     clearOnLoad : true,
13660
13661     /**
13662     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
13663     * property on loading, rather than expecting an array. (eg. more compatible to a standard
13664     * Grid query { data : [ .....] }
13665     */
13666     
13667     root : false,
13668      /**
13669     * @cfg {String} queryParam (optional) 
13670     * Name of the query as it will be passed on the querystring (defaults to 'node')
13671     * eg. the request will be ?node=[id]
13672     */
13673     
13674     
13675     queryParam: false,
13676     
13677     /**
13678      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
13679      * This is called automatically when a node is expanded, but may be used to reload
13680      * a node (or append new children if the {@link #clearOnLoad} option is false.)
13681      * @param {Roo.tree.TreeNode} node
13682      * @param {Function} callback
13683      */
13684     load : function(node, callback){
13685         if(this.clearOnLoad){
13686             while(node.firstChild){
13687                 node.removeChild(node.firstChild);
13688             }
13689         }
13690         if(node.attributes.children){ // preloaded json children
13691             var cs = node.attributes.children;
13692             for(var i = 0, len = cs.length; i < len; i++){
13693                 node.appendChild(this.createNode(cs[i]));
13694             }
13695             if(typeof callback == "function"){
13696                 callback();
13697             }
13698         }else if(this.dataUrl){
13699             this.requestData(node, callback);
13700         }
13701     },
13702
13703     getParams: function(node){
13704         var buf = [], bp = this.baseParams;
13705         for(var key in bp){
13706             if(typeof bp[key] != "function"){
13707                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
13708             }
13709         }
13710         var n = this.queryParam === false ? 'node' : this.queryParam;
13711         buf.push(n + "=", encodeURIComponent(node.id));
13712         return buf.join("");
13713     },
13714
13715     requestData : function(node, callback){
13716         if(this.fireEvent("beforeload", this, node, callback) !== false){
13717             this.transId = Roo.Ajax.request({
13718                 method:this.requestMethod,
13719                 url: this.dataUrl||this.url,
13720                 success: this.handleResponse,
13721                 failure: this.handleFailure,
13722                 scope: this,
13723                 argument: {callback: callback, node: node},
13724                 params: this.getParams(node)
13725             });
13726         }else{
13727             // if the load is cancelled, make sure we notify
13728             // the node that we are done
13729             if(typeof callback == "function"){
13730                 callback();
13731             }
13732         }
13733     },
13734
13735     isLoading : function(){
13736         return this.transId ? true : false;
13737     },
13738
13739     abort : function(){
13740         if(this.isLoading()){
13741             Roo.Ajax.abort(this.transId);
13742         }
13743     },
13744
13745     // private
13746     createNode : function(attr)
13747     {
13748         // apply baseAttrs, nice idea Corey!
13749         if(this.baseAttrs){
13750             Roo.applyIf(attr, this.baseAttrs);
13751         }
13752         if(this.applyLoader !== false){
13753             attr.loader = this;
13754         }
13755         // uiProvider = depreciated..
13756         
13757         if(typeof(attr.uiProvider) == 'string'){
13758            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
13759                 /**  eval:var:attr */ eval(attr.uiProvider);
13760         }
13761         if(typeof(this.uiProviders['default']) != 'undefined') {
13762             attr.uiProvider = this.uiProviders['default'];
13763         }
13764         
13765         this.fireEvent('create', this, attr);
13766         
13767         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
13768         return(attr.leaf ?
13769                         new Roo.tree.TreeNode(attr) :
13770                         new Roo.tree.AsyncTreeNode(attr));
13771     },
13772
13773     processResponse : function(response, node, callback)
13774     {
13775         var json = response.responseText;
13776         try {
13777             
13778             var o = Roo.decode(json);
13779             
13780             if (this.root === false && typeof(o.success) != undefined) {
13781                 this.root = 'data'; // the default behaviour for list like data..
13782                 }
13783                 
13784             if (this.root !== false &&  !o.success) {
13785                 // it's a failure condition.
13786                 var a = response.argument;
13787                 this.fireEvent("loadexception", this, a.node, response);
13788                 Roo.log("Load failed - should have a handler really");
13789                 return;
13790             }
13791             
13792             
13793             
13794             if (this.root !== false) {
13795                  o = o[this.root];
13796             }
13797             
13798             for(var i = 0, len = o.length; i < len; i++){
13799                 var n = this.createNode(o[i]);
13800                 if(n){
13801                     node.appendChild(n);
13802                 }
13803             }
13804             if(typeof callback == "function"){
13805                 callback(this, node);
13806             }
13807         }catch(e){
13808             this.handleFailure(response);
13809         }
13810     },
13811
13812     handleResponse : function(response){
13813         this.transId = false;
13814         var a = response.argument;
13815         this.processResponse(response, a.node, a.callback);
13816         this.fireEvent("load", this, a.node, response);
13817     },
13818
13819     handleFailure : function(response)
13820     {
13821         // should handle failure better..
13822         this.transId = false;
13823         var a = response.argument;
13824         this.fireEvent("loadexception", this, a.node, response);
13825         if(typeof a.callback == "function"){
13826             a.callback(this, a.node);
13827         }
13828     }
13829 });/*
13830  * Based on:
13831  * Ext JS Library 1.1.1
13832  * Copyright(c) 2006-2007, Ext JS, LLC.
13833  *
13834  * Originally Released Under LGPL - original licence link has changed is not relivant.
13835  *
13836  * Fork - LGPL
13837  * <script type="text/javascript">
13838  */
13839
13840 /**
13841 * @class Roo.tree.TreeFilter
13842 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
13843 * @param {TreePanel} tree
13844 * @param {Object} config (optional)
13845  */
13846 Roo.tree.TreeFilter = function(tree, config){
13847     this.tree = tree;
13848     this.filtered = {};
13849     Roo.apply(this, config);
13850 };
13851
13852 Roo.tree.TreeFilter.prototype = {
13853     clearBlank:false,
13854     reverse:false,
13855     autoClear:false,
13856     remove:false,
13857
13858      /**
13859      * Filter the data by a specific attribute.
13860      * @param {String/RegExp} value Either string that the attribute value
13861      * should start with or a RegExp to test against the attribute
13862      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
13863      * @param {TreeNode} startNode (optional) The node to start the filter at.
13864      */
13865     filter : function(value, attr, startNode){
13866         attr = attr || "text";
13867         var f;
13868         if(typeof value == "string"){
13869             var vlen = value.length;
13870             // auto clear empty filter
13871             if(vlen == 0 && this.clearBlank){
13872                 this.clear();
13873                 return;
13874             }
13875             value = value.toLowerCase();
13876             f = function(n){
13877                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
13878             };
13879         }else if(value.exec){ // regex?
13880             f = function(n){
13881                 return value.test(n.attributes[attr]);
13882             };
13883         }else{
13884             throw 'Illegal filter type, must be string or regex';
13885         }
13886         this.filterBy(f, null, startNode);
13887         },
13888
13889     /**
13890      * Filter by a function. The passed function will be called with each
13891      * node in the tree (or from the startNode). If the function returns true, the node is kept
13892      * otherwise it is filtered. If a node is filtered, its children are also filtered.
13893      * @param {Function} fn The filter function
13894      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
13895      */
13896     filterBy : function(fn, scope, startNode){
13897         startNode = startNode || this.tree.root;
13898         if(this.autoClear){
13899             this.clear();
13900         }
13901         var af = this.filtered, rv = this.reverse;
13902         var f = function(n){
13903             if(n == startNode){
13904                 return true;
13905             }
13906             if(af[n.id]){
13907                 return false;
13908             }
13909             var m = fn.call(scope || n, n);
13910             if(!m || rv){
13911                 af[n.id] = n;
13912                 n.ui.hide();
13913                 return false;
13914             }
13915             return true;
13916         };
13917         startNode.cascade(f);
13918         if(this.remove){
13919            for(var id in af){
13920                if(typeof id != "function"){
13921                    var n = af[id];
13922                    if(n && n.parentNode){
13923                        n.parentNode.removeChild(n);
13924                    }
13925                }
13926            }
13927         }
13928     },
13929
13930     /**
13931      * Clears the current filter. Note: with the "remove" option
13932      * set a filter cannot be cleared.
13933      */
13934     clear : function(){
13935         var t = this.tree;
13936         var af = this.filtered;
13937         for(var id in af){
13938             if(typeof id != "function"){
13939                 var n = af[id];
13940                 if(n){
13941                     n.ui.show();
13942                 }
13943             }
13944         }
13945         this.filtered = {};
13946     }
13947 };
13948 /*
13949  * Based on:
13950  * Ext JS Library 1.1.1
13951  * Copyright(c) 2006-2007, Ext JS, LLC.
13952  *
13953  * Originally Released Under LGPL - original licence link has changed is not relivant.
13954  *
13955  * Fork - LGPL
13956  * <script type="text/javascript">
13957  */
13958  
13959
13960 /**
13961  * @class Roo.tree.TreeSorter
13962  * Provides sorting of nodes in a TreePanel
13963  * 
13964  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
13965  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
13966  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
13967  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
13968  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
13969  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
13970  * @constructor
13971  * @param {TreePanel} tree
13972  * @param {Object} config
13973  */
13974 Roo.tree.TreeSorter = function(tree, config){
13975     Roo.apply(this, config);
13976     tree.on("beforechildrenrendered", this.doSort, this);
13977     tree.on("append", this.updateSort, this);
13978     tree.on("insert", this.updateSort, this);
13979     
13980     var dsc = this.dir && this.dir.toLowerCase() == "desc";
13981     var p = this.property || "text";
13982     var sortType = this.sortType;
13983     var fs = this.folderSort;
13984     var cs = this.caseSensitive === true;
13985     var leafAttr = this.leafAttr || 'leaf';
13986
13987     this.sortFn = function(n1, n2){
13988         if(fs){
13989             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
13990                 return 1;
13991             }
13992             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
13993                 return -1;
13994             }
13995         }
13996         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
13997         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
13998         if(v1 < v2){
13999                         return dsc ? +1 : -1;
14000                 }else if(v1 > v2){
14001                         return dsc ? -1 : +1;
14002         }else{
14003                 return 0;
14004         }
14005     };
14006 };
14007
14008 Roo.tree.TreeSorter.prototype = {
14009     doSort : function(node){
14010         node.sort(this.sortFn);
14011     },
14012     
14013     compareNodes : function(n1, n2){
14014         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
14015     },
14016     
14017     updateSort : function(tree, node){
14018         if(node.childrenRendered){
14019             this.doSort.defer(1, this, [node]);
14020         }
14021     }
14022 };/*
14023  * Based on:
14024  * Ext JS Library 1.1.1
14025  * Copyright(c) 2006-2007, Ext JS, LLC.
14026  *
14027  * Originally Released Under LGPL - original licence link has changed is not relivant.
14028  *
14029  * Fork - LGPL
14030  * <script type="text/javascript">
14031  */
14032
14033 if(Roo.dd.DropZone){
14034     
14035 Roo.tree.TreeDropZone = function(tree, config){
14036     this.allowParentInsert = false;
14037     this.allowContainerDrop = false;
14038     this.appendOnly = false;
14039     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
14040     this.tree = tree;
14041     this.lastInsertClass = "x-tree-no-status";
14042     this.dragOverData = {};
14043 };
14044
14045 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
14046     ddGroup : "TreeDD",
14047     scroll:  true,
14048     
14049     expandDelay : 1000,
14050     
14051     expandNode : function(node){
14052         if(node.hasChildNodes() && !node.isExpanded()){
14053             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
14054         }
14055     },
14056     
14057     queueExpand : function(node){
14058         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
14059     },
14060     
14061     cancelExpand : function(){
14062         if(this.expandProcId){
14063             clearTimeout(this.expandProcId);
14064             this.expandProcId = false;
14065         }
14066     },
14067     
14068     isValidDropPoint : function(n, pt, dd, e, data){
14069         if(!n || !data){ return false; }
14070         var targetNode = n.node;
14071         var dropNode = data.node;
14072         // default drop rules
14073         if(!(targetNode && targetNode.isTarget && pt)){
14074             return false;
14075         }
14076         if(pt == "append" && targetNode.allowChildren === false){
14077             return false;
14078         }
14079         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
14080             return false;
14081         }
14082         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
14083             return false;
14084         }
14085         // reuse the object
14086         var overEvent = this.dragOverData;
14087         overEvent.tree = this.tree;
14088         overEvent.target = targetNode;
14089         overEvent.data = data;
14090         overEvent.point = pt;
14091         overEvent.source = dd;
14092         overEvent.rawEvent = e;
14093         overEvent.dropNode = dropNode;
14094         overEvent.cancel = false;  
14095         var result = this.tree.fireEvent("nodedragover", overEvent);
14096         return overEvent.cancel === false && result !== false;
14097     },
14098     
14099     getDropPoint : function(e, n, dd)
14100     {
14101         var tn = n.node;
14102         if(tn.isRoot){
14103             return tn.allowChildren !== false ? "append" : false; // always append for root
14104         }
14105         var dragEl = n.ddel;
14106         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
14107         var y = Roo.lib.Event.getPageY(e);
14108         //var noAppend = tn.allowChildren === false || tn.isLeaf();
14109         
14110         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
14111         var noAppend = tn.allowChildren === false;
14112         if(this.appendOnly || tn.parentNode.allowChildren === false){
14113             return noAppend ? false : "append";
14114         }
14115         var noBelow = false;
14116         if(!this.allowParentInsert){
14117             noBelow = tn.hasChildNodes() && tn.isExpanded();
14118         }
14119         var q = (b - t) / (noAppend ? 2 : 3);
14120         if(y >= t && y < (t + q)){
14121             return "above";
14122         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
14123             return "below";
14124         }else{
14125             return "append";
14126         }
14127     },
14128     
14129     onNodeEnter : function(n, dd, e, data)
14130     {
14131         this.cancelExpand();
14132     },
14133     
14134     onNodeOver : function(n, dd, e, data)
14135     {
14136        
14137         var pt = this.getDropPoint(e, n, dd);
14138         var node = n.node;
14139         
14140         // auto node expand check
14141         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
14142             this.queueExpand(node);
14143         }else if(pt != "append"){
14144             this.cancelExpand();
14145         }
14146         
14147         // set the insert point style on the target node
14148         var returnCls = this.dropNotAllowed;
14149         if(this.isValidDropPoint(n, pt, dd, e, data)){
14150            if(pt){
14151                var el = n.ddel;
14152                var cls;
14153                if(pt == "above"){
14154                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
14155                    cls = "x-tree-drag-insert-above";
14156                }else if(pt == "below"){
14157                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
14158                    cls = "x-tree-drag-insert-below";
14159                }else{
14160                    returnCls = "x-tree-drop-ok-append";
14161                    cls = "x-tree-drag-append";
14162                }
14163                if(this.lastInsertClass != cls){
14164                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
14165                    this.lastInsertClass = cls;
14166                }
14167            }
14168        }
14169        return returnCls;
14170     },
14171     
14172     onNodeOut : function(n, dd, e, data){
14173         
14174         this.cancelExpand();
14175         this.removeDropIndicators(n);
14176     },
14177     
14178     onNodeDrop : function(n, dd, e, data){
14179         var point = this.getDropPoint(e, n, dd);
14180         var targetNode = n.node;
14181         targetNode.ui.startDrop();
14182         if(!this.isValidDropPoint(n, point, dd, e, data)){
14183             targetNode.ui.endDrop();
14184             return false;
14185         }
14186         // first try to find the drop node
14187         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
14188         var dropEvent = {
14189             tree : this.tree,
14190             target: targetNode,
14191             data: data,
14192             point: point,
14193             source: dd,
14194             rawEvent: e,
14195             dropNode: dropNode,
14196             cancel: !dropNode   
14197         };
14198         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
14199         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
14200             targetNode.ui.endDrop();
14201             return false;
14202         }
14203         // allow target changing
14204         targetNode = dropEvent.target;
14205         if(point == "append" && !targetNode.isExpanded()){
14206             targetNode.expand(false, null, function(){
14207                 this.completeDrop(dropEvent);
14208             }.createDelegate(this));
14209         }else{
14210             this.completeDrop(dropEvent);
14211         }
14212         return true;
14213     },
14214     
14215     completeDrop : function(de){
14216         var ns = de.dropNode, p = de.point, t = de.target;
14217         if(!(ns instanceof Array)){
14218             ns = [ns];
14219         }
14220         var n;
14221         for(var i = 0, len = ns.length; i < len; i++){
14222             n = ns[i];
14223             if(p == "above"){
14224                 t.parentNode.insertBefore(n, t);
14225             }else if(p == "below"){
14226                 t.parentNode.insertBefore(n, t.nextSibling);
14227             }else{
14228                 t.appendChild(n);
14229             }
14230         }
14231         n.ui.focus();
14232         if(this.tree.hlDrop){
14233             n.ui.highlight();
14234         }
14235         t.ui.endDrop();
14236         this.tree.fireEvent("nodedrop", de);
14237     },
14238     
14239     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
14240         if(this.tree.hlDrop){
14241             dropNode.ui.focus();
14242             dropNode.ui.highlight();
14243         }
14244         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
14245     },
14246     
14247     getTree : function(){
14248         return this.tree;
14249     },
14250     
14251     removeDropIndicators : function(n){
14252         if(n && n.ddel){
14253             var el = n.ddel;
14254             Roo.fly(el).removeClass([
14255                     "x-tree-drag-insert-above",
14256                     "x-tree-drag-insert-below",
14257                     "x-tree-drag-append"]);
14258             this.lastInsertClass = "_noclass";
14259         }
14260     },
14261     
14262     beforeDragDrop : function(target, e, id){
14263         this.cancelExpand();
14264         return true;
14265     },
14266     
14267     afterRepair : function(data){
14268         if(data && Roo.enableFx){
14269             data.node.ui.highlight();
14270         }
14271         this.hideProxy();
14272     } 
14273     
14274 });
14275
14276 }
14277 /*
14278  * Based on:
14279  * Ext JS Library 1.1.1
14280  * Copyright(c) 2006-2007, Ext JS, LLC.
14281  *
14282  * Originally Released Under LGPL - original licence link has changed is not relivant.
14283  *
14284  * Fork - LGPL
14285  * <script type="text/javascript">
14286  */
14287  
14288
14289 if(Roo.dd.DragZone){
14290 Roo.tree.TreeDragZone = function(tree, config){
14291     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
14292     this.tree = tree;
14293 };
14294
14295 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
14296     ddGroup : "TreeDD",
14297    
14298     onBeforeDrag : function(data, e){
14299         var n = data.node;
14300         return n && n.draggable && !n.disabled;
14301     },
14302      
14303     
14304     onInitDrag : function(e){
14305         var data = this.dragData;
14306         this.tree.getSelectionModel().select(data.node);
14307         this.proxy.update("");
14308         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
14309         this.tree.fireEvent("startdrag", this.tree, data.node, e);
14310     },
14311     
14312     getRepairXY : function(e, data){
14313         return data.node.ui.getDDRepairXY();
14314     },
14315     
14316     onEndDrag : function(data, e){
14317         this.tree.fireEvent("enddrag", this.tree, data.node, e);
14318         
14319         
14320     },
14321     
14322     onValidDrop : function(dd, e, id){
14323         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
14324         this.hideProxy();
14325     },
14326     
14327     beforeInvalidDrop : function(e, id){
14328         // this scrolls the original position back into view
14329         var sm = this.tree.getSelectionModel();
14330         sm.clearSelections();
14331         sm.select(this.dragData.node);
14332     }
14333 });
14334 }/*
14335  * Based on:
14336  * Ext JS Library 1.1.1
14337  * Copyright(c) 2006-2007, Ext JS, LLC.
14338  *
14339  * Originally Released Under LGPL - original licence link has changed is not relivant.
14340  *
14341  * Fork - LGPL
14342  * <script type="text/javascript">
14343  */
14344 /**
14345  * @class Roo.tree.TreeEditor
14346  * @extends Roo.Editor
14347  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
14348  * as the editor field.
14349  * @constructor
14350  * @param {Object} config (used to be the tree panel.)
14351  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
14352  * 
14353  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
14354  * @cfg {Roo.form.TextField|Object} field The field configuration
14355  *
14356  * 
14357  */
14358 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
14359     var tree = config;
14360     var field;
14361     if (oldconfig) { // old style..
14362         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
14363     } else {
14364         // new style..
14365         tree = config.tree;
14366         config.field = config.field  || {};
14367         config.field.xtype = 'TextField';
14368         field = Roo.factory(config.field, Roo.form);
14369     }
14370     config = config || {};
14371     
14372     
14373     this.addEvents({
14374         /**
14375          * @event beforenodeedit
14376          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
14377          * false from the handler of this event.
14378          * @param {Editor} this
14379          * @param {Roo.tree.Node} node 
14380          */
14381         "beforenodeedit" : true
14382     });
14383     
14384     //Roo.log(config);
14385     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
14386
14387     this.tree = tree;
14388
14389     tree.on('beforeclick', this.beforeNodeClick, this);
14390     tree.getTreeEl().on('mousedown', this.hide, this);
14391     this.on('complete', this.updateNode, this);
14392     this.on('beforestartedit', this.fitToTree, this);
14393     this.on('startedit', this.bindScroll, this, {delay:10});
14394     this.on('specialkey', this.onSpecialKey, this);
14395 };
14396
14397 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
14398     /**
14399      * @cfg {String} alignment
14400      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
14401      */
14402     alignment: "l-l",
14403     // inherit
14404     autoSize: false,
14405     /**
14406      * @cfg {Boolean} hideEl
14407      * True to hide the bound element while the editor is displayed (defaults to false)
14408      */
14409     hideEl : false,
14410     /**
14411      * @cfg {String} cls
14412      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
14413      */
14414     cls: "x-small-editor x-tree-editor",
14415     /**
14416      * @cfg {Boolean} shim
14417      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
14418      */
14419     shim:false,
14420     // inherit
14421     shadow:"frame",
14422     /**
14423      * @cfg {Number} maxWidth
14424      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
14425      * the containing tree element's size, it will be automatically limited for you to the container width, taking
14426      * scroll and client offsets into account prior to each edit.
14427      */
14428     maxWidth: 250,
14429
14430     editDelay : 350,
14431
14432     // private
14433     fitToTree : function(ed, el){
14434         var td = this.tree.getTreeEl().dom, nd = el.dom;
14435         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
14436             td.scrollLeft = nd.offsetLeft;
14437         }
14438         var w = Math.min(
14439                 this.maxWidth,
14440                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
14441         this.setSize(w, '');
14442         
14443         return this.fireEvent('beforenodeedit', this, this.editNode);
14444         
14445     },
14446
14447     // private
14448     triggerEdit : function(node){
14449         this.completeEdit();
14450         this.editNode = node;
14451         this.startEdit(node.ui.textNode, node.text);
14452     },
14453
14454     // private
14455     bindScroll : function(){
14456         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
14457     },
14458
14459     // private
14460     beforeNodeClick : function(node, e){
14461         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
14462         this.lastClick = new Date();
14463         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
14464             e.stopEvent();
14465             this.triggerEdit(node);
14466             return false;
14467         }
14468         return true;
14469     },
14470
14471     // private
14472     updateNode : function(ed, value){
14473         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
14474         this.editNode.setText(value);
14475     },
14476
14477     // private
14478     onHide : function(){
14479         Roo.tree.TreeEditor.superclass.onHide.call(this);
14480         if(this.editNode){
14481             this.editNode.ui.focus();
14482         }
14483     },
14484
14485     // private
14486     onSpecialKey : function(field, e){
14487         var k = e.getKey();
14488         if(k == e.ESC){
14489             e.stopEvent();
14490             this.cancelEdit();
14491         }else if(k == e.ENTER && !e.hasModifier()){
14492             e.stopEvent();
14493             this.completeEdit();
14494         }
14495     }
14496 });//<Script type="text/javascript">
14497 /*
14498  * Based on:
14499  * Ext JS Library 1.1.1
14500  * Copyright(c) 2006-2007, Ext JS, LLC.
14501  *
14502  * Originally Released Under LGPL - original licence link has changed is not relivant.
14503  *
14504  * Fork - LGPL
14505  * <script type="text/javascript">
14506  */
14507  
14508 /**
14509  * Not documented??? - probably should be...
14510  */
14511
14512 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
14513     //focus: Roo.emptyFn, // prevent odd scrolling behavior
14514     
14515     renderElements : function(n, a, targetNode, bulkRender){
14516         //consel.log("renderElements?");
14517         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
14518
14519         var t = n.getOwnerTree();
14520         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
14521         
14522         var cols = t.columns;
14523         var bw = t.borderWidth;
14524         var c = cols[0];
14525         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
14526          var cb = typeof a.checked == "boolean";
14527         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14528         var colcls = 'x-t-' + tid + '-c0';
14529         var buf = [
14530             '<li class="x-tree-node">',
14531             
14532                 
14533                 '<div class="x-tree-node-el ', a.cls,'">',
14534                     // extran...
14535                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
14536                 
14537                 
14538                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
14539                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
14540                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
14541                            (a.icon ? ' x-tree-node-inline-icon' : ''),
14542                            (a.iconCls ? ' '+a.iconCls : ''),
14543                            '" unselectable="on" />',
14544                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
14545                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
14546                              
14547                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14548                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
14549                             '<span unselectable="on" qtip="' + tx + '">',
14550                              tx,
14551                              '</span></a>' ,
14552                     '</div>',
14553                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14554                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
14555                  ];
14556         for(var i = 1, len = cols.length; i < len; i++){
14557             c = cols[i];
14558             colcls = 'x-t-' + tid + '-c' +i;
14559             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14560             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
14561                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
14562                       "</div>");
14563          }
14564          
14565          buf.push(
14566             '</a>',
14567             '<div class="x-clear"></div></div>',
14568             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
14569             "</li>");
14570         
14571         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
14572             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
14573                                 n.nextSibling.ui.getEl(), buf.join(""));
14574         }else{
14575             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
14576         }
14577         var el = this.wrap.firstChild;
14578         this.elRow = el;
14579         this.elNode = el.firstChild;
14580         this.ranchor = el.childNodes[1];
14581         this.ctNode = this.wrap.childNodes[1];
14582         var cs = el.firstChild.childNodes;
14583         this.indentNode = cs[0];
14584         this.ecNode = cs[1];
14585         this.iconNode = cs[2];
14586         var index = 3;
14587         if(cb){
14588             this.checkbox = cs[3];
14589             index++;
14590         }
14591         this.anchor = cs[index];
14592         
14593         this.textNode = cs[index].firstChild;
14594         
14595         //el.on("click", this.onClick, this);
14596         //el.on("dblclick", this.onDblClick, this);
14597         
14598         
14599        // console.log(this);
14600     },
14601     initEvents : function(){
14602         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
14603         
14604             
14605         var a = this.ranchor;
14606
14607         var el = Roo.get(a);
14608
14609         if(Roo.isOpera){ // opera render bug ignores the CSS
14610             el.setStyle("text-decoration", "none");
14611         }
14612
14613         el.on("click", this.onClick, this);
14614         el.on("dblclick", this.onDblClick, this);
14615         el.on("contextmenu", this.onContextMenu, this);
14616         
14617     },
14618     
14619     /*onSelectedChange : function(state){
14620         if(state){
14621             this.focus();
14622             this.addClass("x-tree-selected");
14623         }else{
14624             //this.blur();
14625             this.removeClass("x-tree-selected");
14626         }
14627     },*/
14628     addClass : function(cls){
14629         if(this.elRow){
14630             Roo.fly(this.elRow).addClass(cls);
14631         }
14632         
14633     },
14634     
14635     
14636     removeClass : function(cls){
14637         if(this.elRow){
14638             Roo.fly(this.elRow).removeClass(cls);
14639         }
14640     }
14641
14642     
14643     
14644 });//<Script type="text/javascript">
14645
14646 /*
14647  * Based on:
14648  * Ext JS Library 1.1.1
14649  * Copyright(c) 2006-2007, Ext JS, LLC.
14650  *
14651  * Originally Released Under LGPL - original licence link has changed is not relivant.
14652  *
14653  * Fork - LGPL
14654  * <script type="text/javascript">
14655  */
14656  
14657
14658 /**
14659  * @class Roo.tree.ColumnTree
14660  * @extends Roo.data.TreePanel
14661  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
14662  * @cfg {int} borderWidth  compined right/left border allowance
14663  * @constructor
14664  * @param {String/HTMLElement/Element} el The container element
14665  * @param {Object} config
14666  */
14667 Roo.tree.ColumnTree =  function(el, config)
14668 {
14669    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
14670    this.addEvents({
14671         /**
14672         * @event resize
14673         * Fire this event on a container when it resizes
14674         * @param {int} w Width
14675         * @param {int} h Height
14676         */
14677        "resize" : true
14678     });
14679     this.on('resize', this.onResize, this);
14680 };
14681
14682 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
14683     //lines:false,
14684     
14685     
14686     borderWidth: Roo.isBorderBox ? 0 : 2, 
14687     headEls : false,
14688     
14689     render : function(){
14690         // add the header.....
14691        
14692         Roo.tree.ColumnTree.superclass.render.apply(this);
14693         
14694         this.el.addClass('x-column-tree');
14695         
14696         this.headers = this.el.createChild(
14697             {cls:'x-tree-headers'},this.innerCt.dom);
14698    
14699         var cols = this.columns, c;
14700         var totalWidth = 0;
14701         this.headEls = [];
14702         var  len = cols.length;
14703         for(var i = 0; i < len; i++){
14704              c = cols[i];
14705              totalWidth += c.width;
14706             this.headEls.push(this.headers.createChild({
14707                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
14708                  cn: {
14709                      cls:'x-tree-hd-text',
14710                      html: c.header
14711                  },
14712                  style:'width:'+(c.width-this.borderWidth)+'px;'
14713              }));
14714         }
14715         this.headers.createChild({cls:'x-clear'});
14716         // prevent floats from wrapping when clipped
14717         this.headers.setWidth(totalWidth);
14718         //this.innerCt.setWidth(totalWidth);
14719         this.innerCt.setStyle({ overflow: 'auto' });
14720         this.onResize(this.width, this.height);
14721              
14722         
14723     },
14724     onResize : function(w,h)
14725     {
14726         this.height = h;
14727         this.width = w;
14728         // resize cols..
14729         this.innerCt.setWidth(this.width);
14730         this.innerCt.setHeight(this.height-20);
14731         
14732         // headers...
14733         var cols = this.columns, c;
14734         var totalWidth = 0;
14735         var expEl = false;
14736         var len = cols.length;
14737         for(var i = 0; i < len; i++){
14738             c = cols[i];
14739             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
14740                 // it's the expander..
14741                 expEl  = this.headEls[i];
14742                 continue;
14743             }
14744             totalWidth += c.width;
14745             
14746         }
14747         if (expEl) {
14748             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
14749         }
14750         this.headers.setWidth(w-20);
14751
14752         
14753         
14754         
14755     }
14756 });
14757 /*
14758  * Based on:
14759  * Ext JS Library 1.1.1
14760  * Copyright(c) 2006-2007, Ext JS, LLC.
14761  *
14762  * Originally Released Under LGPL - original licence link has changed is not relivant.
14763  *
14764  * Fork - LGPL
14765  * <script type="text/javascript">
14766  */
14767  
14768 /**
14769  * @class Roo.menu.Menu
14770  * @extends Roo.util.Observable
14771  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
14772  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
14773  * @constructor
14774  * Creates a new Menu
14775  * @param {Object} config Configuration options
14776  */
14777 Roo.menu.Menu = function(config){
14778     Roo.apply(this, config);
14779     this.id = this.id || Roo.id();
14780     this.addEvents({
14781         /**
14782          * @event beforeshow
14783          * Fires before this menu is displayed
14784          * @param {Roo.menu.Menu} this
14785          */
14786         beforeshow : true,
14787         /**
14788          * @event beforehide
14789          * Fires before this menu is hidden
14790          * @param {Roo.menu.Menu} this
14791          */
14792         beforehide : true,
14793         /**
14794          * @event show
14795          * Fires after this menu is displayed
14796          * @param {Roo.menu.Menu} this
14797          */
14798         show : true,
14799         /**
14800          * @event hide
14801          * Fires after this menu is hidden
14802          * @param {Roo.menu.Menu} this
14803          */
14804         hide : true,
14805         /**
14806          * @event click
14807          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
14808          * @param {Roo.menu.Menu} this
14809          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14810          * @param {Roo.EventObject} e
14811          */
14812         click : true,
14813         /**
14814          * @event mouseover
14815          * Fires when the mouse is hovering over this menu
14816          * @param {Roo.menu.Menu} this
14817          * @param {Roo.EventObject} e
14818          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14819          */
14820         mouseover : true,
14821         /**
14822          * @event mouseout
14823          * Fires when the mouse exits this menu
14824          * @param {Roo.menu.Menu} this
14825          * @param {Roo.EventObject} e
14826          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14827          */
14828         mouseout : true,
14829         /**
14830          * @event itemclick
14831          * Fires when a menu item contained in this menu is clicked
14832          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
14833          * @param {Roo.EventObject} e
14834          */
14835         itemclick: true
14836     });
14837     if (this.registerMenu) {
14838         Roo.menu.MenuMgr.register(this);
14839     }
14840     
14841     var mis = this.items;
14842     this.items = new Roo.util.MixedCollection();
14843     if(mis){
14844         this.add.apply(this, mis);
14845     }
14846 };
14847
14848 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
14849     /**
14850      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
14851      */
14852     minWidth : 120,
14853     /**
14854      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
14855      * for bottom-right shadow (defaults to "sides")
14856      */
14857     shadow : "sides",
14858     /**
14859      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
14860      * this menu (defaults to "tl-tr?")
14861      */
14862     subMenuAlign : "tl-tr?",
14863     /**
14864      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
14865      * relative to its element of origin (defaults to "tl-bl?")
14866      */
14867     defaultAlign : "tl-bl?",
14868     /**
14869      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
14870      */
14871     allowOtherMenus : false,
14872     /**
14873      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
14874      */
14875     registerMenu : true,
14876
14877     hidden:true,
14878
14879     // private
14880     render : function(){
14881         if(this.el){
14882             return;
14883         }
14884         var el = this.el = new Roo.Layer({
14885             cls: "x-menu",
14886             shadow:this.shadow,
14887             constrain: false,
14888             parentEl: this.parentEl || document.body,
14889             zindex:15000
14890         });
14891
14892         this.keyNav = new Roo.menu.MenuNav(this);
14893
14894         if(this.plain){
14895             el.addClass("x-menu-plain");
14896         }
14897         if(this.cls){
14898             el.addClass(this.cls);
14899         }
14900         // generic focus element
14901         this.focusEl = el.createChild({
14902             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
14903         });
14904         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
14905         //disabling touch- as it's causing issues ..
14906         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
14907         ul.on('click'   , this.onClick, this);
14908         
14909         
14910         ul.on("mouseover", this.onMouseOver, this);
14911         ul.on("mouseout", this.onMouseOut, this);
14912         this.items.each(function(item){
14913             if (item.hidden) {
14914                 return;
14915             }
14916             
14917             var li = document.createElement("li");
14918             li.className = "x-menu-list-item";
14919             ul.dom.appendChild(li);
14920             item.render(li, this);
14921         }, this);
14922         this.ul = ul;
14923         this.autoWidth();
14924     },
14925
14926     // private
14927     autoWidth : function(){
14928         var el = this.el, ul = this.ul;
14929         if(!el){
14930             return;
14931         }
14932         var w = this.width;
14933         if(w){
14934             el.setWidth(w);
14935         }else if(Roo.isIE){
14936             el.setWidth(this.minWidth);
14937             var t = el.dom.offsetWidth; // force recalc
14938             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
14939         }
14940     },
14941
14942     // private
14943     delayAutoWidth : function(){
14944         if(this.rendered){
14945             if(!this.awTask){
14946                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
14947             }
14948             this.awTask.delay(20);
14949         }
14950     },
14951
14952     // private
14953     findTargetItem : function(e){
14954         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
14955         if(t && t.menuItemId){
14956             return this.items.get(t.menuItemId);
14957         }
14958     },
14959
14960     // private
14961     onClick : function(e){
14962         Roo.log("menu.onClick");
14963         var t = this.findTargetItem(e);
14964         if(!t){
14965             return;
14966         }
14967         Roo.log(e);
14968         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
14969             if(t == this.activeItem && t.shouldDeactivate(e)){
14970                 this.activeItem.deactivate();
14971                 delete this.activeItem;
14972                 return;
14973             }
14974             if(t.canActivate){
14975                 this.setActiveItem(t, true);
14976             }
14977             return;
14978             
14979             
14980         }
14981         
14982         t.onClick(e);
14983         this.fireEvent("click", this, t, e);
14984     },
14985
14986     // private
14987     setActiveItem : function(item, autoExpand){
14988         if(item != this.activeItem){
14989             if(this.activeItem){
14990                 this.activeItem.deactivate();
14991             }
14992             this.activeItem = item;
14993             item.activate(autoExpand);
14994         }else if(autoExpand){
14995             item.expandMenu();
14996         }
14997     },
14998
14999     // private
15000     tryActivate : function(start, step){
15001         var items = this.items;
15002         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
15003             var item = items.get(i);
15004             if(!item.disabled && item.canActivate){
15005                 this.setActiveItem(item, false);
15006                 return item;
15007             }
15008         }
15009         return false;
15010     },
15011
15012     // private
15013     onMouseOver : function(e){
15014         var t;
15015         if(t = this.findTargetItem(e)){
15016             if(t.canActivate && !t.disabled){
15017                 this.setActiveItem(t, true);
15018             }
15019         }
15020         this.fireEvent("mouseover", this, e, t);
15021     },
15022
15023     // private
15024     onMouseOut : function(e){
15025         var t;
15026         if(t = this.findTargetItem(e)){
15027             if(t == this.activeItem && t.shouldDeactivate(e)){
15028                 this.activeItem.deactivate();
15029                 delete this.activeItem;
15030             }
15031         }
15032         this.fireEvent("mouseout", this, e, t);
15033     },
15034
15035     /**
15036      * Read-only.  Returns true if the menu is currently displayed, else false.
15037      * @type Boolean
15038      */
15039     isVisible : function(){
15040         return this.el && !this.hidden;
15041     },
15042
15043     /**
15044      * Displays this menu relative to another element
15045      * @param {String/HTMLElement/Roo.Element} element The element to align to
15046      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
15047      * the element (defaults to this.defaultAlign)
15048      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
15049      */
15050     show : function(el, pos, parentMenu){
15051         this.parentMenu = parentMenu;
15052         if(!this.el){
15053             this.render();
15054         }
15055         this.fireEvent("beforeshow", this);
15056         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
15057     },
15058
15059     /**
15060      * Displays this menu at a specific xy position
15061      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
15062      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
15063      */
15064     showAt : function(xy, parentMenu, /* private: */_e){
15065         this.parentMenu = parentMenu;
15066         if(!this.el){
15067             this.render();
15068         }
15069         if(_e !== false){
15070             this.fireEvent("beforeshow", this);
15071             xy = this.el.adjustForConstraints(xy);
15072         }
15073         this.el.setXY(xy);
15074         this.el.show();
15075         this.hidden = false;
15076         this.focus();
15077         this.fireEvent("show", this);
15078     },
15079
15080     focus : function(){
15081         if(!this.hidden){
15082             this.doFocus.defer(50, this);
15083         }
15084     },
15085
15086     doFocus : function(){
15087         if(!this.hidden){
15088             this.focusEl.focus();
15089         }
15090     },
15091
15092     /**
15093      * Hides this menu and optionally all parent menus
15094      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
15095      */
15096     hide : function(deep){
15097         if(this.el && this.isVisible()){
15098             this.fireEvent("beforehide", this);
15099             if(this.activeItem){
15100                 this.activeItem.deactivate();
15101                 this.activeItem = null;
15102             }
15103             this.el.hide();
15104             this.hidden = true;
15105             this.fireEvent("hide", this);
15106         }
15107         if(deep === true && this.parentMenu){
15108             this.parentMenu.hide(true);
15109         }
15110     },
15111
15112     /**
15113      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
15114      * Any of the following are valid:
15115      * <ul>
15116      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
15117      * <li>An HTMLElement object which will be converted to a menu item</li>
15118      * <li>A menu item config object that will be created as a new menu item</li>
15119      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
15120      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
15121      * </ul>
15122      * Usage:
15123      * <pre><code>
15124 // Create the menu
15125 var menu = new Roo.menu.Menu();
15126
15127 // Create a menu item to add by reference
15128 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
15129
15130 // Add a bunch of items at once using different methods.
15131 // Only the last item added will be returned.
15132 var item = menu.add(
15133     menuItem,                // add existing item by ref
15134     'Dynamic Item',          // new TextItem
15135     '-',                     // new separator
15136     { text: 'Config Item' }  // new item by config
15137 );
15138 </code></pre>
15139      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
15140      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
15141      */
15142     add : function(){
15143         var a = arguments, l = a.length, item;
15144         for(var i = 0; i < l; i++){
15145             var el = a[i];
15146             if ((typeof(el) == "object") && el.xtype && el.xns) {
15147                 el = Roo.factory(el, Roo.menu);
15148             }
15149             
15150             if(el.render){ // some kind of Item
15151                 item = this.addItem(el);
15152             }else if(typeof el == "string"){ // string
15153                 if(el == "separator" || el == "-"){
15154                     item = this.addSeparator();
15155                 }else{
15156                     item = this.addText(el);
15157                 }
15158             }else if(el.tagName || el.el){ // element
15159                 item = this.addElement(el);
15160             }else if(typeof el == "object"){ // must be menu item config?
15161                 item = this.addMenuItem(el);
15162             }
15163         }
15164         return item;
15165     },
15166
15167     /**
15168      * Returns this menu's underlying {@link Roo.Element} object
15169      * @return {Roo.Element} The element
15170      */
15171     getEl : function(){
15172         if(!this.el){
15173             this.render();
15174         }
15175         return this.el;
15176     },
15177
15178     /**
15179      * Adds a separator bar to the menu
15180      * @return {Roo.menu.Item} The menu item that was added
15181      */
15182     addSeparator : function(){
15183         return this.addItem(new Roo.menu.Separator());
15184     },
15185
15186     /**
15187      * Adds an {@link Roo.Element} object to the menu
15188      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
15189      * @return {Roo.menu.Item} The menu item that was added
15190      */
15191     addElement : function(el){
15192         return this.addItem(new Roo.menu.BaseItem(el));
15193     },
15194
15195     /**
15196      * Adds an existing object based on {@link Roo.menu.Item} to the menu
15197      * @param {Roo.menu.Item} item The menu item to add
15198      * @return {Roo.menu.Item} The menu item that was added
15199      */
15200     addItem : function(item){
15201         this.items.add(item);
15202         if(this.ul){
15203             var li = document.createElement("li");
15204             li.className = "x-menu-list-item";
15205             this.ul.dom.appendChild(li);
15206             item.render(li, this);
15207             this.delayAutoWidth();
15208         }
15209         return item;
15210     },
15211
15212     /**
15213      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
15214      * @param {Object} config A MenuItem config object
15215      * @return {Roo.menu.Item} The menu item that was added
15216      */
15217     addMenuItem : function(config){
15218         if(!(config instanceof Roo.menu.Item)){
15219             if(typeof config.checked == "boolean"){ // must be check menu item config?
15220                 config = new Roo.menu.CheckItem(config);
15221             }else{
15222                 config = new Roo.menu.Item(config);
15223             }
15224         }
15225         return this.addItem(config);
15226     },
15227
15228     /**
15229      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
15230      * @param {String} text The text to display in the menu item
15231      * @return {Roo.menu.Item} The menu item that was added
15232      */
15233     addText : function(text){
15234         return this.addItem(new Roo.menu.TextItem({ text : text }));
15235     },
15236
15237     /**
15238      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
15239      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
15240      * @param {Roo.menu.Item} item The menu item to add
15241      * @return {Roo.menu.Item} The menu item that was added
15242      */
15243     insert : function(index, item){
15244         this.items.insert(index, item);
15245         if(this.ul){
15246             var li = document.createElement("li");
15247             li.className = "x-menu-list-item";
15248             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
15249             item.render(li, this);
15250             this.delayAutoWidth();
15251         }
15252         return item;
15253     },
15254
15255     /**
15256      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
15257      * @param {Roo.menu.Item} item The menu item to remove
15258      */
15259     remove : function(item){
15260         this.items.removeKey(item.id);
15261         item.destroy();
15262     },
15263
15264     /**
15265      * Removes and destroys all items in the menu
15266      */
15267     removeAll : function(){
15268         var f;
15269         while(f = this.items.first()){
15270             this.remove(f);
15271         }
15272     }
15273 });
15274
15275 // MenuNav is a private utility class used internally by the Menu
15276 Roo.menu.MenuNav = function(menu){
15277     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
15278     this.scope = this.menu = menu;
15279 };
15280
15281 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
15282     doRelay : function(e, h){
15283         var k = e.getKey();
15284         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
15285             this.menu.tryActivate(0, 1);
15286             return false;
15287         }
15288         return h.call(this.scope || this, e, this.menu);
15289     },
15290
15291     up : function(e, m){
15292         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
15293             m.tryActivate(m.items.length-1, -1);
15294         }
15295     },
15296
15297     down : function(e, m){
15298         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
15299             m.tryActivate(0, 1);
15300         }
15301     },
15302
15303     right : function(e, m){
15304         if(m.activeItem){
15305             m.activeItem.expandMenu(true);
15306         }
15307     },
15308
15309     left : function(e, m){
15310         m.hide();
15311         if(m.parentMenu && m.parentMenu.activeItem){
15312             m.parentMenu.activeItem.activate();
15313         }
15314     },
15315
15316     enter : function(e, m){
15317         if(m.activeItem){
15318             e.stopPropagation();
15319             m.activeItem.onClick(e);
15320             m.fireEvent("click", this, m.activeItem);
15321             return true;
15322         }
15323     }
15324 });/*
15325  * Based on:
15326  * Ext JS Library 1.1.1
15327  * Copyright(c) 2006-2007, Ext JS, LLC.
15328  *
15329  * Originally Released Under LGPL - original licence link has changed is not relivant.
15330  *
15331  * Fork - LGPL
15332  * <script type="text/javascript">
15333  */
15334  
15335 /**
15336  * @class Roo.menu.MenuMgr
15337  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
15338  * @singleton
15339  */
15340 Roo.menu.MenuMgr = function(){
15341    var menus, active, groups = {}, attached = false, lastShow = new Date();
15342
15343    // private - called when first menu is created
15344    function init(){
15345        menus = {};
15346        active = new Roo.util.MixedCollection();
15347        Roo.get(document).addKeyListener(27, function(){
15348            if(active.length > 0){
15349                hideAll();
15350            }
15351        });
15352    }
15353
15354    // private
15355    function hideAll(){
15356        if(active && active.length > 0){
15357            var c = active.clone();
15358            c.each(function(m){
15359                m.hide();
15360            });
15361        }
15362    }
15363
15364    // private
15365    function onHide(m){
15366        active.remove(m);
15367        if(active.length < 1){
15368            Roo.get(document).un("mousedown", onMouseDown);
15369            attached = false;
15370        }
15371    }
15372
15373    // private
15374    function onShow(m){
15375        var last = active.last();
15376        lastShow = new Date();
15377        active.add(m);
15378        if(!attached){
15379            Roo.get(document).on("mousedown", onMouseDown);
15380            attached = true;
15381        }
15382        if(m.parentMenu){
15383           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
15384           m.parentMenu.activeChild = m;
15385        }else if(last && last.isVisible()){
15386           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
15387        }
15388    }
15389
15390    // private
15391    function onBeforeHide(m){
15392        if(m.activeChild){
15393            m.activeChild.hide();
15394        }
15395        if(m.autoHideTimer){
15396            clearTimeout(m.autoHideTimer);
15397            delete m.autoHideTimer;
15398        }
15399    }
15400
15401    // private
15402    function onBeforeShow(m){
15403        var pm = m.parentMenu;
15404        if(!pm && !m.allowOtherMenus){
15405            hideAll();
15406        }else if(pm && pm.activeChild && active != m){
15407            pm.activeChild.hide();
15408        }
15409    }
15410
15411    // private
15412    function onMouseDown(e){
15413        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
15414            hideAll();
15415        }
15416    }
15417
15418    // private
15419    function onBeforeCheck(mi, state){
15420        if(state){
15421            var g = groups[mi.group];
15422            for(var i = 0, l = g.length; i < l; i++){
15423                if(g[i] != mi){
15424                    g[i].setChecked(false);
15425                }
15426            }
15427        }
15428    }
15429
15430    return {
15431
15432        /**
15433         * Hides all menus that are currently visible
15434         */
15435        hideAll : function(){
15436             hideAll();  
15437        },
15438
15439        // private
15440        register : function(menu){
15441            if(!menus){
15442                init();
15443            }
15444            menus[menu.id] = menu;
15445            menu.on("beforehide", onBeforeHide);
15446            menu.on("hide", onHide);
15447            menu.on("beforeshow", onBeforeShow);
15448            menu.on("show", onShow);
15449            var g = menu.group;
15450            if(g && menu.events["checkchange"]){
15451                if(!groups[g]){
15452                    groups[g] = [];
15453                }
15454                groups[g].push(menu);
15455                menu.on("checkchange", onCheck);
15456            }
15457        },
15458
15459         /**
15460          * Returns a {@link Roo.menu.Menu} object
15461          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
15462          * be used to generate and return a new Menu instance.
15463          */
15464        get : function(menu){
15465            if(typeof menu == "string"){ // menu id
15466                return menus[menu];
15467            }else if(menu.events){  // menu instance
15468                return menu;
15469            }else if(typeof menu.length == 'number'){ // array of menu items?
15470                return new Roo.menu.Menu({items:menu});
15471            }else{ // otherwise, must be a config
15472                return new Roo.menu.Menu(menu);
15473            }
15474        },
15475
15476        // private
15477        unregister : function(menu){
15478            delete menus[menu.id];
15479            menu.un("beforehide", onBeforeHide);
15480            menu.un("hide", onHide);
15481            menu.un("beforeshow", onBeforeShow);
15482            menu.un("show", onShow);
15483            var g = menu.group;
15484            if(g && menu.events["checkchange"]){
15485                groups[g].remove(menu);
15486                menu.un("checkchange", onCheck);
15487            }
15488        },
15489
15490        // private
15491        registerCheckable : function(menuItem){
15492            var g = menuItem.group;
15493            if(g){
15494                if(!groups[g]){
15495                    groups[g] = [];
15496                }
15497                groups[g].push(menuItem);
15498                menuItem.on("beforecheckchange", onBeforeCheck);
15499            }
15500        },
15501
15502        // private
15503        unregisterCheckable : function(menuItem){
15504            var g = menuItem.group;
15505            if(g){
15506                groups[g].remove(menuItem);
15507                menuItem.un("beforecheckchange", onBeforeCheck);
15508            }
15509        }
15510    };
15511 }();/*
15512  * Based on:
15513  * Ext JS Library 1.1.1
15514  * Copyright(c) 2006-2007, Ext JS, LLC.
15515  *
15516  * Originally Released Under LGPL - original licence link has changed is not relivant.
15517  *
15518  * Fork - LGPL
15519  * <script type="text/javascript">
15520  */
15521  
15522
15523 /**
15524  * @class Roo.menu.BaseItem
15525  * @extends Roo.Component
15526  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
15527  * management and base configuration options shared by all menu components.
15528  * @constructor
15529  * Creates a new BaseItem
15530  * @param {Object} config Configuration options
15531  */
15532 Roo.menu.BaseItem = function(config){
15533     Roo.menu.BaseItem.superclass.constructor.call(this, config);
15534
15535     this.addEvents({
15536         /**
15537          * @event click
15538          * Fires when this item is clicked
15539          * @param {Roo.menu.BaseItem} this
15540          * @param {Roo.EventObject} e
15541          */
15542         click: true,
15543         /**
15544          * @event activate
15545          * Fires when this item is activated
15546          * @param {Roo.menu.BaseItem} this
15547          */
15548         activate : true,
15549         /**
15550          * @event deactivate
15551          * Fires when this item is deactivated
15552          * @param {Roo.menu.BaseItem} this
15553          */
15554         deactivate : true
15555     });
15556
15557     if(this.handler){
15558         this.on("click", this.handler, this.scope, true);
15559     }
15560 };
15561
15562 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
15563     /**
15564      * @cfg {Function} handler
15565      * A function that will handle the click event of this menu item (defaults to undefined)
15566      */
15567     /**
15568      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
15569      */
15570     canActivate : false,
15571     
15572      /**
15573      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
15574      */
15575     hidden: false,
15576     
15577     /**
15578      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
15579      */
15580     activeClass : "x-menu-item-active",
15581     /**
15582      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
15583      */
15584     hideOnClick : true,
15585     /**
15586      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
15587      */
15588     hideDelay : 100,
15589
15590     // private
15591     ctype: "Roo.menu.BaseItem",
15592
15593     // private
15594     actionMode : "container",
15595
15596     // private
15597     render : function(container, parentMenu){
15598         this.parentMenu = parentMenu;
15599         Roo.menu.BaseItem.superclass.render.call(this, container);
15600         this.container.menuItemId = this.id;
15601     },
15602
15603     // private
15604     onRender : function(container, position){
15605         this.el = Roo.get(this.el);
15606         container.dom.appendChild(this.el.dom);
15607     },
15608
15609     // private
15610     onClick : function(e){
15611         if(!this.disabled && this.fireEvent("click", this, e) !== false
15612                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
15613             this.handleClick(e);
15614         }else{
15615             e.stopEvent();
15616         }
15617     },
15618
15619     // private
15620     activate : function(){
15621         if(this.disabled){
15622             return false;
15623         }
15624         var li = this.container;
15625         li.addClass(this.activeClass);
15626         this.region = li.getRegion().adjust(2, 2, -2, -2);
15627         this.fireEvent("activate", this);
15628         return true;
15629     },
15630
15631     // private
15632     deactivate : function(){
15633         this.container.removeClass(this.activeClass);
15634         this.fireEvent("deactivate", this);
15635     },
15636
15637     // private
15638     shouldDeactivate : function(e){
15639         return !this.region || !this.region.contains(e.getPoint());
15640     },
15641
15642     // private
15643     handleClick : function(e){
15644         if(this.hideOnClick){
15645             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
15646         }
15647     },
15648
15649     // private
15650     expandMenu : function(autoActivate){
15651         // do nothing
15652     },
15653
15654     // private
15655     hideMenu : function(){
15656         // do nothing
15657     }
15658 });/*
15659  * Based on:
15660  * Ext JS Library 1.1.1
15661  * Copyright(c) 2006-2007, Ext JS, LLC.
15662  *
15663  * Originally Released Under LGPL - original licence link has changed is not relivant.
15664  *
15665  * Fork - LGPL
15666  * <script type="text/javascript">
15667  */
15668  
15669 /**
15670  * @class Roo.menu.Adapter
15671  * @extends Roo.menu.BaseItem
15672  * A base utility class that adapts a non-menu component so that it can be wrapped by a menu item and added to a menu.
15673  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
15674  * @constructor
15675  * Creates a new Adapter
15676  * @param {Object} config Configuration options
15677  */
15678 Roo.menu.Adapter = function(component, config){
15679     Roo.menu.Adapter.superclass.constructor.call(this, config);
15680     this.component = component;
15681 };
15682 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
15683     // private
15684     canActivate : true,
15685
15686     // private
15687     onRender : function(container, position){
15688         this.component.render(container);
15689         this.el = this.component.getEl();
15690     },
15691
15692     // private
15693     activate : function(){
15694         if(this.disabled){
15695             return false;
15696         }
15697         this.component.focus();
15698         this.fireEvent("activate", this);
15699         return true;
15700     },
15701
15702     // private
15703     deactivate : function(){
15704         this.fireEvent("deactivate", this);
15705     },
15706
15707     // private
15708     disable : function(){
15709         this.component.disable();
15710         Roo.menu.Adapter.superclass.disable.call(this);
15711     },
15712
15713     // private
15714     enable : function(){
15715         this.component.enable();
15716         Roo.menu.Adapter.superclass.enable.call(this);
15717     }
15718 });/*
15719  * Based on:
15720  * Ext JS Library 1.1.1
15721  * Copyright(c) 2006-2007, Ext JS, LLC.
15722  *
15723  * Originally Released Under LGPL - original licence link has changed is not relivant.
15724  *
15725  * Fork - LGPL
15726  * <script type="text/javascript">
15727  */
15728
15729 /**
15730  * @class Roo.menu.TextItem
15731  * @extends Roo.menu.BaseItem
15732  * Adds a static text string to a menu, usually used as either a heading or group separator.
15733  * Note: old style constructor with text is still supported.
15734  * 
15735  * @constructor
15736  * Creates a new TextItem
15737  * @param {Object} cfg Configuration
15738  */
15739 Roo.menu.TextItem = function(cfg){
15740     if (typeof(cfg) == 'string') {
15741         this.text = cfg;
15742     } else {
15743         Roo.apply(this,cfg);
15744     }
15745     
15746     Roo.menu.TextItem.superclass.constructor.call(this);
15747 };
15748
15749 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
15750     /**
15751      * @cfg {Boolean} text Text to show on item.
15752      */
15753     text : '',
15754     
15755     /**
15756      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15757      */
15758     hideOnClick : false,
15759     /**
15760      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
15761      */
15762     itemCls : "x-menu-text",
15763
15764     // private
15765     onRender : function(){
15766         var s = document.createElement("span");
15767         s.className = this.itemCls;
15768         s.innerHTML = this.text;
15769         this.el = s;
15770         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
15771     }
15772 });/*
15773  * Based on:
15774  * Ext JS Library 1.1.1
15775  * Copyright(c) 2006-2007, Ext JS, LLC.
15776  *
15777  * Originally Released Under LGPL - original licence link has changed is not relivant.
15778  *
15779  * Fork - LGPL
15780  * <script type="text/javascript">
15781  */
15782
15783 /**
15784  * @class Roo.menu.Separator
15785  * @extends Roo.menu.BaseItem
15786  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
15787  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
15788  * @constructor
15789  * @param {Object} config Configuration options
15790  */
15791 Roo.menu.Separator = function(config){
15792     Roo.menu.Separator.superclass.constructor.call(this, config);
15793 };
15794
15795 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
15796     /**
15797      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
15798      */
15799     itemCls : "x-menu-sep",
15800     /**
15801      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15802      */
15803     hideOnClick : false,
15804
15805     // private
15806     onRender : function(li){
15807         var s = document.createElement("span");
15808         s.className = this.itemCls;
15809         s.innerHTML = "&#160;";
15810         this.el = s;
15811         li.addClass("x-menu-sep-li");
15812         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
15813     }
15814 });/*
15815  * Based on:
15816  * Ext JS Library 1.1.1
15817  * Copyright(c) 2006-2007, Ext JS, LLC.
15818  *
15819  * Originally Released Under LGPL - original licence link has changed is not relivant.
15820  *
15821  * Fork - LGPL
15822  * <script type="text/javascript">
15823  */
15824 /**
15825  * @class Roo.menu.Item
15826  * @extends Roo.menu.BaseItem
15827  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
15828  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
15829  * activation and click handling.
15830  * @constructor
15831  * Creates a new Item
15832  * @param {Object} config Configuration options
15833  */
15834 Roo.menu.Item = function(config){
15835     Roo.menu.Item.superclass.constructor.call(this, config);
15836     if(this.menu){
15837         this.menu = Roo.menu.MenuMgr.get(this.menu);
15838     }
15839 };
15840 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
15841     
15842     /**
15843      * @cfg {String} text
15844      * The text to show on the menu item.
15845      */
15846     text: '',
15847      /**
15848      * @cfg {String} HTML to render in menu
15849      * The text to show on the menu item (HTML version).
15850      */
15851     html: '',
15852     /**
15853      * @cfg {String} icon
15854      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
15855      */
15856     icon: undefined,
15857     /**
15858      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
15859      */
15860     itemCls : "x-menu-item",
15861     /**
15862      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
15863      */
15864     canActivate : true,
15865     /**
15866      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
15867      */
15868     showDelay: 200,
15869     // doc'd in BaseItem
15870     hideDelay: 200,
15871
15872     // private
15873     ctype: "Roo.menu.Item",
15874     
15875     // private
15876     onRender : function(container, position){
15877         var el = document.createElement("a");
15878         el.hideFocus = true;
15879         el.unselectable = "on";
15880         el.href = this.href || "#";
15881         if(this.hrefTarget){
15882             el.target = this.hrefTarget;
15883         }
15884         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
15885         
15886         var html = this.html.length ? this.html  : String.format('{0}',this.text);
15887         
15888         el.innerHTML = String.format(
15889                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
15890                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
15891         this.el = el;
15892         Roo.menu.Item.superclass.onRender.call(this, container, position);
15893     },
15894
15895     /**
15896      * Sets the text to display in this menu item
15897      * @param {String} text The text to display
15898      * @param {Boolean} isHTML true to indicate text is pure html.
15899      */
15900     setText : function(text, isHTML){
15901         if (isHTML) {
15902             this.html = text;
15903         } else {
15904             this.text = text;
15905             this.html = '';
15906         }
15907         if(this.rendered){
15908             var html = this.html.length ? this.html  : String.format('{0}',this.text);
15909      
15910             this.el.update(String.format(
15911                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
15912                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
15913             this.parentMenu.autoWidth();
15914         }
15915     },
15916
15917     // private
15918     handleClick : function(e){
15919         if(!this.href){ // if no link defined, stop the event automatically
15920             e.stopEvent();
15921         }
15922         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
15923     },
15924
15925     // private
15926     activate : function(autoExpand){
15927         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
15928             this.focus();
15929             if(autoExpand){
15930                 this.expandMenu();
15931             }
15932         }
15933         return true;
15934     },
15935
15936     // private
15937     shouldDeactivate : function(e){
15938         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
15939             if(this.menu && this.menu.isVisible()){
15940                 return !this.menu.getEl().getRegion().contains(e.getPoint());
15941             }
15942             return true;
15943         }
15944         return false;
15945     },
15946
15947     // private
15948     deactivate : function(){
15949         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
15950         this.hideMenu();
15951     },
15952
15953     // private
15954     expandMenu : function(autoActivate){
15955         if(!this.disabled && this.menu){
15956             clearTimeout(this.hideTimer);
15957             delete this.hideTimer;
15958             if(!this.menu.isVisible() && !this.showTimer){
15959                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
15960             }else if (this.menu.isVisible() && autoActivate){
15961                 this.menu.tryActivate(0, 1);
15962             }
15963         }
15964     },
15965
15966     // private
15967     deferExpand : function(autoActivate){
15968         delete this.showTimer;
15969         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
15970         if(autoActivate){
15971             this.menu.tryActivate(0, 1);
15972         }
15973     },
15974
15975     // private
15976     hideMenu : function(){
15977         clearTimeout(this.showTimer);
15978         delete this.showTimer;
15979         if(!this.hideTimer && this.menu && this.menu.isVisible()){
15980             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
15981         }
15982     },
15983
15984     // private
15985     deferHide : function(){
15986         delete this.hideTimer;
15987         this.menu.hide();
15988     }
15989 });/*
15990  * Based on:
15991  * Ext JS Library 1.1.1
15992  * Copyright(c) 2006-2007, Ext JS, LLC.
15993  *
15994  * Originally Released Under LGPL - original licence link has changed is not relivant.
15995  *
15996  * Fork - LGPL
15997  * <script type="text/javascript">
15998  */
15999  
16000 /**
16001  * @class Roo.menu.CheckItem
16002  * @extends Roo.menu.Item
16003  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
16004  * @constructor
16005  * Creates a new CheckItem
16006  * @param {Object} config Configuration options
16007  */
16008 Roo.menu.CheckItem = function(config){
16009     Roo.menu.CheckItem.superclass.constructor.call(this, config);
16010     this.addEvents({
16011         /**
16012          * @event beforecheckchange
16013          * Fires before the checked value is set, providing an opportunity to cancel if needed
16014          * @param {Roo.menu.CheckItem} this
16015          * @param {Boolean} checked The new checked value that will be set
16016          */
16017         "beforecheckchange" : true,
16018         /**
16019          * @event checkchange
16020          * Fires after the checked value has been set
16021          * @param {Roo.menu.CheckItem} this
16022          * @param {Boolean} checked The checked value that was set
16023          */
16024         "checkchange" : true
16025     });
16026     if(this.checkHandler){
16027         this.on('checkchange', this.checkHandler, this.scope);
16028     }
16029 };
16030 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
16031     /**
16032      * @cfg {String} group
16033      * All check items with the same group name will automatically be grouped into a single-select
16034      * radio button group (defaults to '')
16035      */
16036     /**
16037      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
16038      */
16039     itemCls : "x-menu-item x-menu-check-item",
16040     /**
16041      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
16042      */
16043     groupClass : "x-menu-group-item",
16044
16045     /**
16046      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
16047      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
16048      * initialized with checked = true will be rendered as checked.
16049      */
16050     checked: false,
16051
16052     // private
16053     ctype: "Roo.menu.CheckItem",
16054
16055     // private
16056     onRender : function(c){
16057         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
16058         if(this.group){
16059             this.el.addClass(this.groupClass);
16060         }
16061         Roo.menu.MenuMgr.registerCheckable(this);
16062         if(this.checked){
16063             this.checked = false;
16064             this.setChecked(true, true);
16065         }
16066     },
16067
16068     // private
16069     destroy : function(){
16070         if(this.rendered){
16071             Roo.menu.MenuMgr.unregisterCheckable(this);
16072         }
16073         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
16074     },
16075
16076     /**
16077      * Set the checked state of this item
16078      * @param {Boolean} checked The new checked value
16079      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
16080      */
16081     setChecked : function(state, suppressEvent){
16082         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
16083             if(this.container){
16084                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
16085             }
16086             this.checked = state;
16087             if(suppressEvent !== true){
16088                 this.fireEvent("checkchange", this, state);
16089             }
16090         }
16091     },
16092
16093     // private
16094     handleClick : function(e){
16095        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
16096            this.setChecked(!this.checked);
16097        }
16098        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
16099     }
16100 });/*
16101  * Based on:
16102  * Ext JS Library 1.1.1
16103  * Copyright(c) 2006-2007, Ext JS, LLC.
16104  *
16105  * Originally Released Under LGPL - original licence link has changed is not relivant.
16106  *
16107  * Fork - LGPL
16108  * <script type="text/javascript">
16109  */
16110  
16111 /**
16112  * @class Roo.menu.DateItem
16113  * @extends Roo.menu.Adapter
16114  * A menu item that wraps the {@link Roo.DatPicker} component.
16115  * @constructor
16116  * Creates a new DateItem
16117  * @param {Object} config Configuration options
16118  */
16119 Roo.menu.DateItem = function(config){
16120     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
16121     /** The Roo.DatePicker object @type Roo.DatePicker */
16122     this.picker = this.component;
16123     this.addEvents({select: true});
16124     
16125     this.picker.on("render", function(picker){
16126         picker.getEl().swallowEvent("click");
16127         picker.container.addClass("x-menu-date-item");
16128     });
16129
16130     this.picker.on("select", this.onSelect, this);
16131 };
16132
16133 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
16134     // private
16135     onSelect : function(picker, date){
16136         this.fireEvent("select", this, date, picker);
16137         Roo.menu.DateItem.superclass.handleClick.call(this);
16138     }
16139 });/*
16140  * Based on:
16141  * Ext JS Library 1.1.1
16142  * Copyright(c) 2006-2007, Ext JS, LLC.
16143  *
16144  * Originally Released Under LGPL - original licence link has changed is not relivant.
16145  *
16146  * Fork - LGPL
16147  * <script type="text/javascript">
16148  */
16149  
16150 /**
16151  * @class Roo.menu.ColorItem
16152  * @extends Roo.menu.Adapter
16153  * A menu item that wraps the {@link Roo.ColorPalette} component.
16154  * @constructor
16155  * Creates a new ColorItem
16156  * @param {Object} config Configuration options
16157  */
16158 Roo.menu.ColorItem = function(config){
16159     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
16160     /** The Roo.ColorPalette object @type Roo.ColorPalette */
16161     this.palette = this.component;
16162     this.relayEvents(this.palette, ["select"]);
16163     if(this.selectHandler){
16164         this.on('select', this.selectHandler, this.scope);
16165     }
16166 };
16167 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
16168  * Based on:
16169  * Ext JS Library 1.1.1
16170  * Copyright(c) 2006-2007, Ext JS, LLC.
16171  *
16172  * Originally Released Under LGPL - original licence link has changed is not relivant.
16173  *
16174  * Fork - LGPL
16175  * <script type="text/javascript">
16176  */
16177  
16178
16179 /**
16180  * @class Roo.menu.DateMenu
16181  * @extends Roo.menu.Menu
16182  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
16183  * @constructor
16184  * Creates a new DateMenu
16185  * @param {Object} config Configuration options
16186  */
16187 Roo.menu.DateMenu = function(config){
16188     Roo.menu.DateMenu.superclass.constructor.call(this, config);
16189     this.plain = true;
16190     var di = new Roo.menu.DateItem(config);
16191     this.add(di);
16192     /**
16193      * The {@link Roo.DatePicker} instance for this DateMenu
16194      * @type DatePicker
16195      */
16196     this.picker = di.picker;
16197     /**
16198      * @event select
16199      * @param {DatePicker} picker
16200      * @param {Date} date
16201      */
16202     this.relayEvents(di, ["select"]);
16203     this.on('beforeshow', function(){
16204         if(this.picker){
16205             this.picker.hideMonthPicker(false);
16206         }
16207     }, this);
16208 };
16209 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
16210     cls:'x-date-menu'
16211 });/*
16212  * Based on:
16213  * Ext JS Library 1.1.1
16214  * Copyright(c) 2006-2007, Ext JS, LLC.
16215  *
16216  * Originally Released Under LGPL - original licence link has changed is not relivant.
16217  *
16218  * Fork - LGPL
16219  * <script type="text/javascript">
16220  */
16221  
16222
16223 /**
16224  * @class Roo.menu.ColorMenu
16225  * @extends Roo.menu.Menu
16226  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
16227  * @constructor
16228  * Creates a new ColorMenu
16229  * @param {Object} config Configuration options
16230  */
16231 Roo.menu.ColorMenu = function(config){
16232     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
16233     this.plain = true;
16234     var ci = new Roo.menu.ColorItem(config);
16235     this.add(ci);
16236     /**
16237      * The {@link Roo.ColorPalette} instance for this ColorMenu
16238      * @type ColorPalette
16239      */
16240     this.palette = ci.palette;
16241     /**
16242      * @event select
16243      * @param {ColorPalette} palette
16244      * @param {String} color
16245      */
16246     this.relayEvents(ci, ["select"]);
16247 };
16248 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
16249  * Based on:
16250  * Ext JS Library 1.1.1
16251  * Copyright(c) 2006-2007, Ext JS, LLC.
16252  *
16253  * Originally Released Under LGPL - original licence link has changed is not relivant.
16254  *
16255  * Fork - LGPL
16256  * <script type="text/javascript">
16257  */
16258  
16259 /**
16260  * @class Roo.form.TextItem
16261  * @extends Roo.BoxComponent
16262  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
16263  * @constructor
16264  * Creates a new TextItem
16265  * @param {Object} config Configuration options
16266  */
16267 Roo.form.TextItem = function(config){
16268     Roo.form.TextItem.superclass.constructor.call(this, config);
16269 };
16270
16271 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
16272     
16273     /**
16274      * @cfg {String} tag the tag for this item (default div)
16275      */
16276     tag : 'div',
16277     /**
16278      * @cfg {String} html the content for this item
16279      */
16280     html : '',
16281     
16282     getAutoCreate : function()
16283     {
16284         var cfg = {
16285             id: this.id,
16286             tag: this.tag,
16287             html: this.html,
16288             cls: 'x-form-item'
16289         };
16290         
16291         return cfg;
16292         
16293     },
16294     
16295     onRender : function(ct, position)
16296     {
16297         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
16298         
16299         if(!this.el){
16300             var cfg = this.getAutoCreate();
16301             if(!cfg.name){
16302                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
16303             }
16304             if (!cfg.name.length) {
16305                 delete cfg.name;
16306             }
16307             this.el = ct.createChild(cfg, position);
16308         }
16309     }
16310     
16311 });/*
16312  * Based on:
16313  * Ext JS Library 1.1.1
16314  * Copyright(c) 2006-2007, Ext JS, LLC.
16315  *
16316  * Originally Released Under LGPL - original licence link has changed is not relivant.
16317  *
16318  * Fork - LGPL
16319  * <script type="text/javascript">
16320  */
16321  
16322 /**
16323  * @class Roo.form.Field
16324  * @extends Roo.BoxComponent
16325  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
16326  * @constructor
16327  * Creates a new Field
16328  * @param {Object} config Configuration options
16329  */
16330 Roo.form.Field = function(config){
16331     Roo.form.Field.superclass.constructor.call(this, config);
16332 };
16333
16334 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
16335     /**
16336      * @cfg {String} fieldLabel Label to use when rendering a form.
16337      */
16338        /**
16339      * @cfg {String} qtip Mouse over tip
16340      */
16341      
16342     /**
16343      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
16344      */
16345     invalidClass : "x-form-invalid",
16346     /**
16347      * @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")
16348      */
16349     invalidText : "The value in this field is invalid",
16350     /**
16351      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
16352      */
16353     focusClass : "x-form-focus",
16354     /**
16355      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
16356       automatic validation (defaults to "keyup").
16357      */
16358     validationEvent : "keyup",
16359     /**
16360      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
16361      */
16362     validateOnBlur : true,
16363     /**
16364      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
16365      */
16366     validationDelay : 250,
16367     /**
16368      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
16369      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
16370      */
16371     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
16372     /**
16373      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
16374      */
16375     fieldClass : "x-form-field",
16376     /**
16377      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
16378      *<pre>
16379 Value         Description
16380 -----------   ----------------------------------------------------------------------
16381 qtip          Display a quick tip when the user hovers over the field
16382 title         Display a default browser title attribute popup
16383 under         Add a block div beneath the field containing the error text
16384 side          Add an error icon to the right of the field with a popup on hover
16385 [element id]  Add the error text directly to the innerHTML of the specified element
16386 </pre>
16387      */
16388     msgTarget : 'qtip',
16389     /**
16390      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
16391      */
16392     msgFx : 'normal',
16393
16394     /**
16395      * @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.
16396      */
16397     readOnly : false,
16398
16399     /**
16400      * @cfg {Boolean} disabled True to disable the field (defaults to false).
16401      */
16402     disabled : false,
16403
16404     /**
16405      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
16406      */
16407     inputType : undefined,
16408     
16409     /**
16410      * @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).
16411          */
16412         tabIndex : undefined,
16413         
16414     // private
16415     isFormField : true,
16416
16417     // private
16418     hasFocus : false,
16419     /**
16420      * @property {Roo.Element} fieldEl
16421      * Element Containing the rendered Field (with label etc.)
16422      */
16423     /**
16424      * @cfg {Mixed} value A value to initialize this field with.
16425      */
16426     value : undefined,
16427
16428     /**
16429      * @cfg {String} name The field's HTML name attribute.
16430      */
16431     /**
16432      * @cfg {String} cls A CSS class to apply to the field's underlying element.
16433      */
16434     // private
16435     loadedValue : false,
16436      
16437      
16438         // private ??
16439         initComponent : function(){
16440         Roo.form.Field.superclass.initComponent.call(this);
16441         this.addEvents({
16442             /**
16443              * @event focus
16444              * Fires when this field receives input focus.
16445              * @param {Roo.form.Field} this
16446              */
16447             focus : true,
16448             /**
16449              * @event blur
16450              * Fires when this field loses input focus.
16451              * @param {Roo.form.Field} this
16452              */
16453             blur : true,
16454             /**
16455              * @event specialkey
16456              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
16457              * {@link Roo.EventObject#getKey} to determine which key was pressed.
16458              * @param {Roo.form.Field} this
16459              * @param {Roo.EventObject} e The event object
16460              */
16461             specialkey : true,
16462             /**
16463              * @event change
16464              * Fires just before the field blurs if the field value has changed.
16465              * @param {Roo.form.Field} this
16466              * @param {Mixed} newValue The new value
16467              * @param {Mixed} oldValue The original value
16468              */
16469             change : true,
16470             /**
16471              * @event invalid
16472              * Fires after the field has been marked as invalid.
16473              * @param {Roo.form.Field} this
16474              * @param {String} msg The validation message
16475              */
16476             invalid : true,
16477             /**
16478              * @event valid
16479              * Fires after the field has been validated with no errors.
16480              * @param {Roo.form.Field} this
16481              */
16482             valid : true,
16483              /**
16484              * @event keyup
16485              * Fires after the key up
16486              * @param {Roo.form.Field} this
16487              * @param {Roo.EventObject}  e The event Object
16488              */
16489             keyup : true
16490         });
16491     },
16492
16493     /**
16494      * Returns the name attribute of the field if available
16495      * @return {String} name The field name
16496      */
16497     getName: function(){
16498          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
16499     },
16500
16501     // private
16502     onRender : function(ct, position){
16503         Roo.form.Field.superclass.onRender.call(this, ct, position);
16504         if(!this.el){
16505             var cfg = this.getAutoCreate();
16506             if(!cfg.name){
16507                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
16508             }
16509             if (!cfg.name.length) {
16510                 delete cfg.name;
16511             }
16512             if(this.inputType){
16513                 cfg.type = this.inputType;
16514             }
16515             this.el = ct.createChild(cfg, position);
16516         }
16517         var type = this.el.dom.type;
16518         if(type){
16519             if(type == 'password'){
16520                 type = 'text';
16521             }
16522             this.el.addClass('x-form-'+type);
16523         }
16524         if(this.readOnly){
16525             this.el.dom.readOnly = true;
16526         }
16527         if(this.tabIndex !== undefined){
16528             this.el.dom.setAttribute('tabIndex', this.tabIndex);
16529         }
16530
16531         this.el.addClass([this.fieldClass, this.cls]);
16532         this.initValue();
16533     },
16534
16535     /**
16536      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
16537      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
16538      * @return {Roo.form.Field} this
16539      */
16540     applyTo : function(target){
16541         this.allowDomMove = false;
16542         this.el = Roo.get(target);
16543         this.render(this.el.dom.parentNode);
16544         return this;
16545     },
16546
16547     // private
16548     initValue : function(){
16549         if(this.value !== undefined){
16550             this.setValue(this.value);
16551         }else if(this.el.dom.value.length > 0){
16552             this.setValue(this.el.dom.value);
16553         }
16554     },
16555
16556     /**
16557      * Returns true if this field has been changed since it was originally loaded and is not disabled.
16558      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
16559      */
16560     isDirty : function() {
16561         if(this.disabled) {
16562             return false;
16563         }
16564         return String(this.getValue()) !== String(this.originalValue);
16565     },
16566
16567     /**
16568      * stores the current value in loadedValue
16569      */
16570     resetHasChanged : function()
16571     {
16572         this.loadedValue = String(this.getValue());
16573     },
16574     /**
16575      * checks the current value against the 'loaded' value.
16576      * Note - will return false if 'resetHasChanged' has not been called first.
16577      */
16578     hasChanged : function()
16579     {
16580         if(this.disabled || this.readOnly) {
16581             return false;
16582         }
16583         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
16584     },
16585     
16586     
16587     
16588     // private
16589     afterRender : function(){
16590         Roo.form.Field.superclass.afterRender.call(this);
16591         this.initEvents();
16592     },
16593
16594     // private
16595     fireKey : function(e){
16596         //Roo.log('field ' + e.getKey());
16597         if(e.isNavKeyPress()){
16598             this.fireEvent("specialkey", this, e);
16599         }
16600     },
16601
16602     /**
16603      * Resets the current field value to the originally loaded value and clears any validation messages
16604      */
16605     reset : function(){
16606         this.setValue(this.resetValue);
16607         this.originalValue = this.getValue();
16608         this.clearInvalid();
16609     },
16610
16611     // private
16612     initEvents : function(){
16613         // safari killled keypress - so keydown is now used..
16614         this.el.on("keydown" , this.fireKey,  this);
16615         this.el.on("focus", this.onFocus,  this);
16616         this.el.on("blur", this.onBlur,  this);
16617         this.el.relayEvent('keyup', this);
16618
16619         // reference to original value for reset
16620         this.originalValue = this.getValue();
16621         this.resetValue =  this.getValue();
16622     },
16623
16624     // private
16625     onFocus : function(){
16626         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16627             this.el.addClass(this.focusClass);
16628         }
16629         if(!this.hasFocus){
16630             this.hasFocus = true;
16631             this.startValue = this.getValue();
16632             this.fireEvent("focus", this);
16633         }
16634     },
16635
16636     beforeBlur : Roo.emptyFn,
16637
16638     // private
16639     onBlur : function(){
16640         this.beforeBlur();
16641         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16642             this.el.removeClass(this.focusClass);
16643         }
16644         this.hasFocus = false;
16645         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
16646             this.validate();
16647         }
16648         var v = this.getValue();
16649         if(String(v) !== String(this.startValue)){
16650             this.fireEvent('change', this, v, this.startValue);
16651         }
16652         this.fireEvent("blur", this);
16653     },
16654
16655     /**
16656      * Returns whether or not the field value is currently valid
16657      * @param {Boolean} preventMark True to disable marking the field invalid
16658      * @return {Boolean} True if the value is valid, else false
16659      */
16660     isValid : function(preventMark){
16661         if(this.disabled){
16662             return true;
16663         }
16664         var restore = this.preventMark;
16665         this.preventMark = preventMark === true;
16666         var v = this.validateValue(this.processValue(this.getRawValue()));
16667         this.preventMark = restore;
16668         return v;
16669     },
16670
16671     /**
16672      * Validates the field value
16673      * @return {Boolean} True if the value is valid, else false
16674      */
16675     validate : function(){
16676         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
16677             this.clearInvalid();
16678             return true;
16679         }
16680         return false;
16681     },
16682
16683     processValue : function(value){
16684         return value;
16685     },
16686
16687     // private
16688     // Subclasses should provide the validation implementation by overriding this
16689     validateValue : function(value){
16690         return true;
16691     },
16692
16693     /**
16694      * Mark this field as invalid
16695      * @param {String} msg The validation message
16696      */
16697     markInvalid : function(msg){
16698         if(!this.rendered || this.preventMark){ // not rendered
16699             return;
16700         }
16701         
16702         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16703         
16704         obj.el.addClass(this.invalidClass);
16705         msg = msg || this.invalidText;
16706         switch(this.msgTarget){
16707             case 'qtip':
16708                 obj.el.dom.qtip = msg;
16709                 obj.el.dom.qclass = 'x-form-invalid-tip';
16710                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
16711                     Roo.QuickTips.enable();
16712                 }
16713                 break;
16714             case 'title':
16715                 this.el.dom.title = msg;
16716                 break;
16717             case 'under':
16718                 if(!this.errorEl){
16719                     var elp = this.el.findParent('.x-form-element', 5, true);
16720                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
16721                     this.errorEl.setWidth(elp.getWidth(true)-20);
16722                 }
16723                 this.errorEl.update(msg);
16724                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
16725                 break;
16726             case 'side':
16727                 if(!this.errorIcon){
16728                     var elp = this.el.findParent('.x-form-element', 5, true);
16729                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
16730                 }
16731                 this.alignErrorIcon();
16732                 this.errorIcon.dom.qtip = msg;
16733                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
16734                 this.errorIcon.show();
16735                 this.on('resize', this.alignErrorIcon, this);
16736                 break;
16737             default:
16738                 var t = Roo.getDom(this.msgTarget);
16739                 t.innerHTML = msg;
16740                 t.style.display = this.msgDisplay;
16741                 break;
16742         }
16743         this.fireEvent('invalid', this, msg);
16744     },
16745
16746     // private
16747     alignErrorIcon : function(){
16748         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
16749     },
16750
16751     /**
16752      * Clear any invalid styles/messages for this field
16753      */
16754     clearInvalid : function(){
16755         if(!this.rendered || this.preventMark){ // not rendered
16756             return;
16757         }
16758         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16759         
16760         obj.el.removeClass(this.invalidClass);
16761         switch(this.msgTarget){
16762             case 'qtip':
16763                 obj.el.dom.qtip = '';
16764                 break;
16765             case 'title':
16766                 this.el.dom.title = '';
16767                 break;
16768             case 'under':
16769                 if(this.errorEl){
16770                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
16771                 }
16772                 break;
16773             case 'side':
16774                 if(this.errorIcon){
16775                     this.errorIcon.dom.qtip = '';
16776                     this.errorIcon.hide();
16777                     this.un('resize', this.alignErrorIcon, this);
16778                 }
16779                 break;
16780             default:
16781                 var t = Roo.getDom(this.msgTarget);
16782                 t.innerHTML = '';
16783                 t.style.display = 'none';
16784                 break;
16785         }
16786         this.fireEvent('valid', this);
16787     },
16788
16789     /**
16790      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
16791      * @return {Mixed} value The field value
16792      */
16793     getRawValue : function(){
16794         var v = this.el.getValue();
16795         
16796         return v;
16797     },
16798
16799     /**
16800      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16801      * @return {Mixed} value The field value
16802      */
16803     getValue : function(){
16804         var v = this.el.getValue();
16805          
16806         return v;
16807     },
16808
16809     /**
16810      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
16811      * @param {Mixed} value The value to set
16812      */
16813     setRawValue : function(v){
16814         return this.el.dom.value = (v === null || v === undefined ? '' : v);
16815     },
16816
16817     /**
16818      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
16819      * @param {Mixed} value The value to set
16820      */
16821     setValue : function(v){
16822         this.value = v;
16823         if(this.rendered){
16824             this.el.dom.value = (v === null || v === undefined ? '' : v);
16825              this.validate();
16826         }
16827     },
16828
16829     adjustSize : function(w, h){
16830         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
16831         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
16832         return s;
16833     },
16834
16835     adjustWidth : function(tag, w){
16836         tag = tag.toLowerCase();
16837         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
16838             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
16839                 if(tag == 'input'){
16840                     return w + 2;
16841                 }
16842                 if(tag == 'textarea'){
16843                     return w-2;
16844                 }
16845             }else if(Roo.isOpera){
16846                 if(tag == 'input'){
16847                     return w + 2;
16848                 }
16849                 if(tag == 'textarea'){
16850                     return w-2;
16851                 }
16852             }
16853         }
16854         return w;
16855     }
16856 });
16857
16858
16859 // anything other than normal should be considered experimental
16860 Roo.form.Field.msgFx = {
16861     normal : {
16862         show: function(msgEl, f){
16863             msgEl.setDisplayed('block');
16864         },
16865
16866         hide : function(msgEl, f){
16867             msgEl.setDisplayed(false).update('');
16868         }
16869     },
16870
16871     slide : {
16872         show: function(msgEl, f){
16873             msgEl.slideIn('t', {stopFx:true});
16874         },
16875
16876         hide : function(msgEl, f){
16877             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
16878         }
16879     },
16880
16881     slideRight : {
16882         show: function(msgEl, f){
16883             msgEl.fixDisplay();
16884             msgEl.alignTo(f.el, 'tl-tr');
16885             msgEl.slideIn('l', {stopFx:true});
16886         },
16887
16888         hide : function(msgEl, f){
16889             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
16890         }
16891     }
16892 };/*
16893  * Based on:
16894  * Ext JS Library 1.1.1
16895  * Copyright(c) 2006-2007, Ext JS, LLC.
16896  *
16897  * Originally Released Under LGPL - original licence link has changed is not relivant.
16898  *
16899  * Fork - LGPL
16900  * <script type="text/javascript">
16901  */
16902  
16903
16904 /**
16905  * @class Roo.form.TextField
16906  * @extends Roo.form.Field
16907  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
16908  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
16909  * @constructor
16910  * Creates a new TextField
16911  * @param {Object} config Configuration options
16912  */
16913 Roo.form.TextField = function(config){
16914     Roo.form.TextField.superclass.constructor.call(this, config);
16915     this.addEvents({
16916         /**
16917          * @event autosize
16918          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
16919          * according to the default logic, but this event provides a hook for the developer to apply additional
16920          * logic at runtime to resize the field if needed.
16921              * @param {Roo.form.Field} this This text field
16922              * @param {Number} width The new field width
16923              */
16924         autosize : true
16925     });
16926 };
16927
16928 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
16929     /**
16930      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
16931      */
16932     grow : false,
16933     /**
16934      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
16935      */
16936     growMin : 30,
16937     /**
16938      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
16939      */
16940     growMax : 800,
16941     /**
16942      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
16943      */
16944     vtype : null,
16945     /**
16946      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
16947      */
16948     maskRe : null,
16949     /**
16950      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
16951      */
16952     disableKeyFilter : false,
16953     /**
16954      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
16955      */
16956     allowBlank : true,
16957     /**
16958      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
16959      */
16960     minLength : 0,
16961     /**
16962      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
16963      */
16964     maxLength : Number.MAX_VALUE,
16965     /**
16966      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
16967      */
16968     minLengthText : "The minimum length for this field is {0}",
16969     /**
16970      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
16971      */
16972     maxLengthText : "The maximum length for this field is {0}",
16973     /**
16974      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
16975      */
16976     selectOnFocus : false,
16977     /**
16978      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
16979      */
16980     blankText : "This field is required",
16981     /**
16982      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
16983      * If available, this function will be called only after the basic validators all return true, and will be passed the
16984      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
16985      */
16986     validator : null,
16987     /**
16988      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
16989      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
16990      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
16991      */
16992     regex : null,
16993     /**
16994      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
16995      */
16996     regexText : "",
16997     /**
16998      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
16999      */
17000     emptyText : null,
17001    
17002
17003     // private
17004     initEvents : function()
17005     {
17006         if (this.emptyText) {
17007             this.el.attr('placeholder', this.emptyText);
17008         }
17009         
17010         Roo.form.TextField.superclass.initEvents.call(this);
17011         if(this.validationEvent == 'keyup'){
17012             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
17013             this.el.on('keyup', this.filterValidation, this);
17014         }
17015         else if(this.validationEvent !== false){
17016             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
17017         }
17018         
17019         if(this.selectOnFocus){
17020             this.on("focus", this.preFocus, this);
17021             
17022         }
17023         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
17024             this.el.on("keypress", this.filterKeys, this);
17025         }
17026         if(this.grow){
17027             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
17028             this.el.on("click", this.autoSize,  this);
17029         }
17030         if(this.el.is('input[type=password]') && Roo.isSafari){
17031             this.el.on('keydown', this.SafariOnKeyDown, this);
17032         }
17033     },
17034
17035     processValue : function(value){
17036         if(this.stripCharsRe){
17037             var newValue = value.replace(this.stripCharsRe, '');
17038             if(newValue !== value){
17039                 this.setRawValue(newValue);
17040                 return newValue;
17041             }
17042         }
17043         return value;
17044     },
17045
17046     filterValidation : function(e){
17047         if(!e.isNavKeyPress()){
17048             this.validationTask.delay(this.validationDelay);
17049         }
17050     },
17051
17052     // private
17053     onKeyUp : function(e){
17054         if(!e.isNavKeyPress()){
17055             this.autoSize();
17056         }
17057     },
17058
17059     /**
17060      * Resets the current field value to the originally-loaded value and clears any validation messages.
17061      *  
17062      */
17063     reset : function(){
17064         Roo.form.TextField.superclass.reset.call(this);
17065        
17066     },
17067
17068     
17069     // private
17070     preFocus : function(){
17071         
17072         if(this.selectOnFocus){
17073             this.el.dom.select();
17074         }
17075     },
17076
17077     
17078     // private
17079     filterKeys : function(e){
17080         var k = e.getKey();
17081         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
17082             return;
17083         }
17084         var c = e.getCharCode(), cc = String.fromCharCode(c);
17085         if(Roo.isIE && (e.isSpecialKey() || !cc)){
17086             return;
17087         }
17088         if(!this.maskRe.test(cc)){
17089             e.stopEvent();
17090         }
17091     },
17092
17093     setValue : function(v){
17094         
17095         Roo.form.TextField.superclass.setValue.apply(this, arguments);
17096         
17097         this.autoSize();
17098     },
17099
17100     /**
17101      * Validates a value according to the field's validation rules and marks the field as invalid
17102      * if the validation fails
17103      * @param {Mixed} value The value to validate
17104      * @return {Boolean} True if the value is valid, else false
17105      */
17106     validateValue : function(value){
17107         if(value.length < 1)  { // if it's blank
17108              if(this.allowBlank){
17109                 this.clearInvalid();
17110                 return true;
17111              }else{
17112                 this.markInvalid(this.blankText);
17113                 return false;
17114              }
17115         }
17116         if(value.length < this.minLength){
17117             this.markInvalid(String.format(this.minLengthText, this.minLength));
17118             return false;
17119         }
17120         if(value.length > this.maxLength){
17121             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
17122             return false;
17123         }
17124         if(this.vtype){
17125             var vt = Roo.form.VTypes;
17126             if(!vt[this.vtype](value, this)){
17127                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
17128                 return false;
17129             }
17130         }
17131         if(typeof this.validator == "function"){
17132             var msg = this.validator(value);
17133             if(msg !== true){
17134                 this.markInvalid(msg);
17135                 return false;
17136             }
17137         }
17138         if(this.regex && !this.regex.test(value)){
17139             this.markInvalid(this.regexText);
17140             return false;
17141         }
17142         return true;
17143     },
17144
17145     /**
17146      * Selects text in this field
17147      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
17148      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
17149      */
17150     selectText : function(start, end){
17151         var v = this.getRawValue();
17152         if(v.length > 0){
17153             start = start === undefined ? 0 : start;
17154             end = end === undefined ? v.length : end;
17155             var d = this.el.dom;
17156             if(d.setSelectionRange){
17157                 d.setSelectionRange(start, end);
17158             }else if(d.createTextRange){
17159                 var range = d.createTextRange();
17160                 range.moveStart("character", start);
17161                 range.moveEnd("character", v.length-end);
17162                 range.select();
17163             }
17164         }
17165     },
17166
17167     /**
17168      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
17169      * This only takes effect if grow = true, and fires the autosize event.
17170      */
17171     autoSize : function(){
17172         if(!this.grow || !this.rendered){
17173             return;
17174         }
17175         if(!this.metrics){
17176             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
17177         }
17178         var el = this.el;
17179         var v = el.dom.value;
17180         var d = document.createElement('div');
17181         d.appendChild(document.createTextNode(v));
17182         v = d.innerHTML;
17183         d = null;
17184         v += "&#160;";
17185         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
17186         this.el.setWidth(w);
17187         this.fireEvent("autosize", this, w);
17188     },
17189     
17190     // private
17191     SafariOnKeyDown : function(event)
17192     {
17193         // this is a workaround for a password hang bug on chrome/ webkit.
17194         
17195         var isSelectAll = false;
17196         
17197         if(this.el.dom.selectionEnd > 0){
17198             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
17199         }
17200         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
17201             event.preventDefault();
17202             this.setValue('');
17203             return;
17204         }
17205         
17206         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
17207             
17208             event.preventDefault();
17209             // this is very hacky as keydown always get's upper case.
17210             
17211             var cc = String.fromCharCode(event.getCharCode());
17212             
17213             
17214             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
17215             
17216         }
17217         
17218         
17219     }
17220 });/*
17221  * Based on:
17222  * Ext JS Library 1.1.1
17223  * Copyright(c) 2006-2007, Ext JS, LLC.
17224  *
17225  * Originally Released Under LGPL - original licence link has changed is not relivant.
17226  *
17227  * Fork - LGPL
17228  * <script type="text/javascript">
17229  */
17230  
17231 /**
17232  * @class Roo.form.Hidden
17233  * @extends Roo.form.TextField
17234  * Simple Hidden element used on forms 
17235  * 
17236  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
17237  * 
17238  * @constructor
17239  * Creates a new Hidden form element.
17240  * @param {Object} config Configuration options
17241  */
17242
17243
17244
17245 // easy hidden field...
17246 Roo.form.Hidden = function(config){
17247     Roo.form.Hidden.superclass.constructor.call(this, config);
17248 };
17249   
17250 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
17251     fieldLabel:      '',
17252     inputType:      'hidden',
17253     width:          50,
17254     allowBlank:     true,
17255     labelSeparator: '',
17256     hidden:         true,
17257     itemCls :       'x-form-item-display-none'
17258
17259
17260 });
17261
17262
17263 /*
17264  * Based on:
17265  * Ext JS Library 1.1.1
17266  * Copyright(c) 2006-2007, Ext JS, LLC.
17267  *
17268  * Originally Released Under LGPL - original licence link has changed is not relivant.
17269  *
17270  * Fork - LGPL
17271  * <script type="text/javascript">
17272  */
17273  
17274 /**
17275  * @class Roo.form.TriggerField
17276  * @extends Roo.form.TextField
17277  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
17278  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
17279  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
17280  * for which you can provide a custom implementation.  For example:
17281  * <pre><code>
17282 var trigger = new Roo.form.TriggerField();
17283 trigger.onTriggerClick = myTriggerFn;
17284 trigger.applyTo('my-field');
17285 </code></pre>
17286  *
17287  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
17288  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
17289  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
17290  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
17291  * @constructor
17292  * Create a new TriggerField.
17293  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
17294  * to the base TextField)
17295  */
17296 Roo.form.TriggerField = function(config){
17297     this.mimicing = false;
17298     Roo.form.TriggerField.superclass.constructor.call(this, config);
17299 };
17300
17301 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
17302     /**
17303      * @cfg {String} triggerClass A CSS class to apply to the trigger
17304      */
17305     /**
17306      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17307      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
17308      */
17309     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
17310     /**
17311      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
17312      */
17313     hideTrigger:false,
17314
17315     /** @cfg {Boolean} grow @hide */
17316     /** @cfg {Number} growMin @hide */
17317     /** @cfg {Number} growMax @hide */
17318
17319     /**
17320      * @hide 
17321      * @method
17322      */
17323     autoSize: Roo.emptyFn,
17324     // private
17325     monitorTab : true,
17326     // private
17327     deferHeight : true,
17328
17329     
17330     actionMode : 'wrap',
17331     // private
17332     onResize : function(w, h){
17333         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
17334         if(typeof w == 'number'){
17335             var x = w - this.trigger.getWidth();
17336             this.el.setWidth(this.adjustWidth('input', x));
17337             this.trigger.setStyle('left', x+'px');
17338         }
17339     },
17340
17341     // private
17342     adjustSize : Roo.BoxComponent.prototype.adjustSize,
17343
17344     // private
17345     getResizeEl : function(){
17346         return this.wrap;
17347     },
17348
17349     // private
17350     getPositionEl : function(){
17351         return this.wrap;
17352     },
17353
17354     // private
17355     alignErrorIcon : function(){
17356         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
17357     },
17358
17359     // private
17360     onRender : function(ct, position){
17361         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
17362         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
17363         this.trigger = this.wrap.createChild(this.triggerConfig ||
17364                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
17365         if(this.hideTrigger){
17366             this.trigger.setDisplayed(false);
17367         }
17368         this.initTrigger();
17369         if(!this.width){
17370             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
17371         }
17372     },
17373
17374     // private
17375     initTrigger : function(){
17376         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
17377         this.trigger.addClassOnOver('x-form-trigger-over');
17378         this.trigger.addClassOnClick('x-form-trigger-click');
17379     },
17380
17381     // private
17382     onDestroy : function(){
17383         if(this.trigger){
17384             this.trigger.removeAllListeners();
17385             this.trigger.remove();
17386         }
17387         if(this.wrap){
17388             this.wrap.remove();
17389         }
17390         Roo.form.TriggerField.superclass.onDestroy.call(this);
17391     },
17392
17393     // private
17394     onFocus : function(){
17395         Roo.form.TriggerField.superclass.onFocus.call(this);
17396         if(!this.mimicing){
17397             this.wrap.addClass('x-trigger-wrap-focus');
17398             this.mimicing = true;
17399             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
17400             if(this.monitorTab){
17401                 this.el.on("keydown", this.checkTab, this);
17402             }
17403         }
17404     },
17405
17406     // private
17407     checkTab : function(e){
17408         if(e.getKey() == e.TAB){
17409             this.triggerBlur();
17410         }
17411     },
17412
17413     // private
17414     onBlur : function(){
17415         // do nothing
17416     },
17417
17418     // private
17419     mimicBlur : function(e, t){
17420         if(!this.wrap.contains(t) && this.validateBlur()){
17421             this.triggerBlur();
17422         }
17423     },
17424
17425     // private
17426     triggerBlur : function(){
17427         this.mimicing = false;
17428         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
17429         if(this.monitorTab){
17430             this.el.un("keydown", this.checkTab, this);
17431         }
17432         this.wrap.removeClass('x-trigger-wrap-focus');
17433         Roo.form.TriggerField.superclass.onBlur.call(this);
17434     },
17435
17436     // private
17437     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
17438     validateBlur : function(e, t){
17439         return true;
17440     },
17441
17442     // private
17443     onDisable : function(){
17444         Roo.form.TriggerField.superclass.onDisable.call(this);
17445         if(this.wrap){
17446             this.wrap.addClass('x-item-disabled');
17447         }
17448     },
17449
17450     // private
17451     onEnable : function(){
17452         Roo.form.TriggerField.superclass.onEnable.call(this);
17453         if(this.wrap){
17454             this.wrap.removeClass('x-item-disabled');
17455         }
17456     },
17457
17458     // private
17459     onShow : function(){
17460         var ae = this.getActionEl();
17461         
17462         if(ae){
17463             ae.dom.style.display = '';
17464             ae.dom.style.visibility = 'visible';
17465         }
17466     },
17467
17468     // private
17469     
17470     onHide : function(){
17471         var ae = this.getActionEl();
17472         ae.dom.style.display = 'none';
17473     },
17474
17475     /**
17476      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
17477      * by an implementing function.
17478      * @method
17479      * @param {EventObject} e
17480      */
17481     onTriggerClick : Roo.emptyFn
17482 });
17483
17484 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
17485 // to be extended by an implementing class.  For an example of implementing this class, see the custom
17486 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
17487 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
17488     initComponent : function(){
17489         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
17490
17491         this.triggerConfig = {
17492             tag:'span', cls:'x-form-twin-triggers', cn:[
17493             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
17494             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
17495         ]};
17496     },
17497
17498     getTrigger : function(index){
17499         return this.triggers[index];
17500     },
17501
17502     initTrigger : function(){
17503         var ts = this.trigger.select('.x-form-trigger', true);
17504         this.wrap.setStyle('overflow', 'hidden');
17505         var triggerField = this;
17506         ts.each(function(t, all, index){
17507             t.hide = function(){
17508                 var w = triggerField.wrap.getWidth();
17509                 this.dom.style.display = 'none';
17510                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17511             };
17512             t.show = function(){
17513                 var w = triggerField.wrap.getWidth();
17514                 this.dom.style.display = '';
17515                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17516             };
17517             var triggerIndex = 'Trigger'+(index+1);
17518
17519             if(this['hide'+triggerIndex]){
17520                 t.dom.style.display = 'none';
17521             }
17522             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
17523             t.addClassOnOver('x-form-trigger-over');
17524             t.addClassOnClick('x-form-trigger-click');
17525         }, this);
17526         this.triggers = ts.elements;
17527     },
17528
17529     onTrigger1Click : Roo.emptyFn,
17530     onTrigger2Click : Roo.emptyFn
17531 });/*
17532  * Based on:
17533  * Ext JS Library 1.1.1
17534  * Copyright(c) 2006-2007, Ext JS, LLC.
17535  *
17536  * Originally Released Under LGPL - original licence link has changed is not relivant.
17537  *
17538  * Fork - LGPL
17539  * <script type="text/javascript">
17540  */
17541  
17542 /**
17543  * @class Roo.form.TextArea
17544  * @extends Roo.form.TextField
17545  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
17546  * support for auto-sizing.
17547  * @constructor
17548  * Creates a new TextArea
17549  * @param {Object} config Configuration options
17550  */
17551 Roo.form.TextArea = function(config){
17552     Roo.form.TextArea.superclass.constructor.call(this, config);
17553     // these are provided exchanges for backwards compat
17554     // minHeight/maxHeight were replaced by growMin/growMax to be
17555     // compatible with TextField growing config values
17556     if(this.minHeight !== undefined){
17557         this.growMin = this.minHeight;
17558     }
17559     if(this.maxHeight !== undefined){
17560         this.growMax = this.maxHeight;
17561     }
17562 };
17563
17564 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
17565     /**
17566      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
17567      */
17568     growMin : 60,
17569     /**
17570      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
17571      */
17572     growMax: 1000,
17573     /**
17574      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
17575      * in the field (equivalent to setting overflow: hidden, defaults to false)
17576      */
17577     preventScrollbars: false,
17578     /**
17579      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17580      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
17581      */
17582
17583     // private
17584     onRender : function(ct, position){
17585         if(!this.el){
17586             this.defaultAutoCreate = {
17587                 tag: "textarea",
17588                 style:"width:300px;height:60px;",
17589                 autocomplete: "new-password"
17590             };
17591         }
17592         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
17593         if(this.grow){
17594             this.textSizeEl = Roo.DomHelper.append(document.body, {
17595                 tag: "pre", cls: "x-form-grow-sizer"
17596             });
17597             if(this.preventScrollbars){
17598                 this.el.setStyle("overflow", "hidden");
17599             }
17600             this.el.setHeight(this.growMin);
17601         }
17602     },
17603
17604     onDestroy : function(){
17605         if(this.textSizeEl){
17606             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
17607         }
17608         Roo.form.TextArea.superclass.onDestroy.call(this);
17609     },
17610
17611     // private
17612     onKeyUp : function(e){
17613         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
17614             this.autoSize();
17615         }
17616     },
17617
17618     /**
17619      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
17620      * This only takes effect if grow = true, and fires the autosize event if the height changes.
17621      */
17622     autoSize : function(){
17623         if(!this.grow || !this.textSizeEl){
17624             return;
17625         }
17626         var el = this.el;
17627         var v = el.dom.value;
17628         var ts = this.textSizeEl;
17629
17630         ts.innerHTML = '';
17631         ts.appendChild(document.createTextNode(v));
17632         v = ts.innerHTML;
17633
17634         Roo.fly(ts).setWidth(this.el.getWidth());
17635         if(v.length < 1){
17636             v = "&#160;&#160;";
17637         }else{
17638             if(Roo.isIE){
17639                 v = v.replace(/\n/g, '<p>&#160;</p>');
17640             }
17641             v += "&#160;\n&#160;";
17642         }
17643         ts.innerHTML = v;
17644         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
17645         if(h != this.lastHeight){
17646             this.lastHeight = h;
17647             this.el.setHeight(h);
17648             this.fireEvent("autosize", this, h);
17649         }
17650     }
17651 });/*
17652  * Based on:
17653  * Ext JS Library 1.1.1
17654  * Copyright(c) 2006-2007, Ext JS, LLC.
17655  *
17656  * Originally Released Under LGPL - original licence link has changed is not relivant.
17657  *
17658  * Fork - LGPL
17659  * <script type="text/javascript">
17660  */
17661  
17662
17663 /**
17664  * @class Roo.form.NumberField
17665  * @extends Roo.form.TextField
17666  * Numeric text field that provides automatic keystroke filtering and numeric validation.
17667  * @constructor
17668  * Creates a new NumberField
17669  * @param {Object} config Configuration options
17670  */
17671 Roo.form.NumberField = function(config){
17672     Roo.form.NumberField.superclass.constructor.call(this, config);
17673 };
17674
17675 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
17676     /**
17677      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
17678      */
17679     fieldClass: "x-form-field x-form-num-field",
17680     /**
17681      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
17682      */
17683     allowDecimals : true,
17684     /**
17685      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
17686      */
17687     decimalSeparator : ".",
17688     /**
17689      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
17690      */
17691     decimalPrecision : 2,
17692     /**
17693      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
17694      */
17695     allowNegative : true,
17696     /**
17697      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
17698      */
17699     minValue : Number.NEGATIVE_INFINITY,
17700     /**
17701      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
17702      */
17703     maxValue : Number.MAX_VALUE,
17704     /**
17705      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
17706      */
17707     minText : "The minimum value for this field is {0}",
17708     /**
17709      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
17710      */
17711     maxText : "The maximum value for this field is {0}",
17712     /**
17713      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
17714      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
17715      */
17716     nanText : "{0} is not a valid number",
17717
17718     // private
17719     initEvents : function(){
17720         Roo.form.NumberField.superclass.initEvents.call(this);
17721         var allowed = "0123456789";
17722         if(this.allowDecimals){
17723             allowed += this.decimalSeparator;
17724         }
17725         if(this.allowNegative){
17726             allowed += "-";
17727         }
17728         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
17729         var keyPress = function(e){
17730             var k = e.getKey();
17731             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
17732                 return;
17733             }
17734             var c = e.getCharCode();
17735             if(allowed.indexOf(String.fromCharCode(c)) === -1){
17736                 e.stopEvent();
17737             }
17738         };
17739         this.el.on("keypress", keyPress, this);
17740     },
17741
17742     // private
17743     validateValue : function(value){
17744         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
17745             return false;
17746         }
17747         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17748              return true;
17749         }
17750         var num = this.parseValue(value);
17751         if(isNaN(num)){
17752             this.markInvalid(String.format(this.nanText, value));
17753             return false;
17754         }
17755         if(num < this.minValue){
17756             this.markInvalid(String.format(this.minText, this.minValue));
17757             return false;
17758         }
17759         if(num > this.maxValue){
17760             this.markInvalid(String.format(this.maxText, this.maxValue));
17761             return false;
17762         }
17763         return true;
17764     },
17765
17766     getValue : function(){
17767         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
17768     },
17769
17770     // private
17771     parseValue : function(value){
17772         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
17773         return isNaN(value) ? '' : value;
17774     },
17775
17776     // private
17777     fixPrecision : function(value){
17778         var nan = isNaN(value);
17779         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
17780             return nan ? '' : value;
17781         }
17782         return parseFloat(value).toFixed(this.decimalPrecision);
17783     },
17784
17785     setValue : function(v){
17786         v = this.fixPrecision(v);
17787         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
17788     },
17789
17790     // private
17791     decimalPrecisionFcn : function(v){
17792         return Math.floor(v);
17793     },
17794
17795     beforeBlur : function(){
17796         var v = this.parseValue(this.getRawValue());
17797         if(v){
17798             this.setValue(v);
17799         }
17800     }
17801 });/*
17802  * Based on:
17803  * Ext JS Library 1.1.1
17804  * Copyright(c) 2006-2007, Ext JS, LLC.
17805  *
17806  * Originally Released Under LGPL - original licence link has changed is not relivant.
17807  *
17808  * Fork - LGPL
17809  * <script type="text/javascript">
17810  */
17811  
17812 /**
17813  * @class Roo.form.DateField
17814  * @extends Roo.form.TriggerField
17815  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17816 * @constructor
17817 * Create a new DateField
17818 * @param {Object} config
17819  */
17820 Roo.form.DateField = function(config){
17821     Roo.form.DateField.superclass.constructor.call(this, config);
17822     
17823       this.addEvents({
17824          
17825         /**
17826          * @event select
17827          * Fires when a date is selected
17828              * @param {Roo.form.DateField} combo This combo box
17829              * @param {Date} date The date selected
17830              */
17831         'select' : true
17832          
17833     });
17834     
17835     
17836     if(typeof this.minValue == "string") {
17837         this.minValue = this.parseDate(this.minValue);
17838     }
17839     if(typeof this.maxValue == "string") {
17840         this.maxValue = this.parseDate(this.maxValue);
17841     }
17842     this.ddMatch = null;
17843     if(this.disabledDates){
17844         var dd = this.disabledDates;
17845         var re = "(?:";
17846         for(var i = 0; i < dd.length; i++){
17847             re += dd[i];
17848             if(i != dd.length-1) {
17849                 re += "|";
17850             }
17851         }
17852         this.ddMatch = new RegExp(re + ")");
17853     }
17854 };
17855
17856 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
17857     /**
17858      * @cfg {String} format
17859      * The default date format string which can be overriden for localization support.  The format must be
17860      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17861      */
17862     format : "m/d/y",
17863     /**
17864      * @cfg {String} altFormats
17865      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17866      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17867      */
17868     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17869     /**
17870      * @cfg {Array} disabledDays
17871      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17872      */
17873     disabledDays : null,
17874     /**
17875      * @cfg {String} disabledDaysText
17876      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17877      */
17878     disabledDaysText : "Disabled",
17879     /**
17880      * @cfg {Array} disabledDates
17881      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17882      * expression so they are very powerful. Some examples:
17883      * <ul>
17884      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17885      * <li>["03/08", "09/16"] would disable those days for every year</li>
17886      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17887      * <li>["03/../2006"] would disable every day in March 2006</li>
17888      * <li>["^03"] would disable every day in every March</li>
17889      * </ul>
17890      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17891      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17892      */
17893     disabledDates : null,
17894     /**
17895      * @cfg {String} disabledDatesText
17896      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17897      */
17898     disabledDatesText : "Disabled",
17899     /**
17900      * @cfg {Date/String} minValue
17901      * The minimum allowed date. Can be either a Javascript date object or a string date in a
17902      * valid format (defaults to null).
17903      */
17904     minValue : null,
17905     /**
17906      * @cfg {Date/String} maxValue
17907      * The maximum allowed date. Can be either a Javascript date object or a string date in a
17908      * valid format (defaults to null).
17909      */
17910     maxValue : null,
17911     /**
17912      * @cfg {String} minText
17913      * The error text to display when the date in the cell is before minValue (defaults to
17914      * 'The date in this field must be after {minValue}').
17915      */
17916     minText : "The date in this field must be equal to or after {0}",
17917     /**
17918      * @cfg {String} maxText
17919      * The error text to display when the date in the cell is after maxValue (defaults to
17920      * 'The date in this field must be before {maxValue}').
17921      */
17922     maxText : "The date in this field must be equal to or before {0}",
17923     /**
17924      * @cfg {String} invalidText
17925      * The error text to display when the date in the field is invalid (defaults to
17926      * '{value} is not a valid date - it must be in the format {format}').
17927      */
17928     invalidText : "{0} is not a valid date - it must be in the format {1}",
17929     /**
17930      * @cfg {String} triggerClass
17931      * An additional CSS class used to style the trigger button.  The trigger will always get the
17932      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
17933      * which displays a calendar icon).
17934      */
17935     triggerClass : 'x-form-date-trigger',
17936     
17937
17938     /**
17939      * @cfg {Boolean} useIso
17940      * if enabled, then the date field will use a hidden field to store the 
17941      * real value as iso formated date. default (false)
17942      */ 
17943     useIso : false,
17944     /**
17945      * @cfg {String/Object} autoCreate
17946      * A DomHelper element spec, or true for a default element spec (defaults to
17947      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
17948      */ 
17949     // private
17950     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
17951     
17952     // private
17953     hiddenField: false,
17954     
17955     onRender : function(ct, position)
17956     {
17957         Roo.form.DateField.superclass.onRender.call(this, ct, position);
17958         if (this.useIso) {
17959             //this.el.dom.removeAttribute('name'); 
17960             Roo.log("Changing name?");
17961             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
17962             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
17963                     'before', true);
17964             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
17965             // prevent input submission
17966             this.hiddenName = this.name;
17967         }
17968             
17969             
17970     },
17971     
17972     // private
17973     validateValue : function(value)
17974     {
17975         value = this.formatDate(value);
17976         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
17977             Roo.log('super failed');
17978             return false;
17979         }
17980         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17981              return true;
17982         }
17983         var svalue = value;
17984         value = this.parseDate(value);
17985         if(!value){
17986             Roo.log('parse date failed' + svalue);
17987             this.markInvalid(String.format(this.invalidText, svalue, this.format));
17988             return false;
17989         }
17990         var time = value.getTime();
17991         if(this.minValue && time < this.minValue.getTime()){
17992             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
17993             return false;
17994         }
17995         if(this.maxValue && time > this.maxValue.getTime()){
17996             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
17997             return false;
17998         }
17999         if(this.disabledDays){
18000             var day = value.getDay();
18001             for(var i = 0; i < this.disabledDays.length; i++) {
18002                 if(day === this.disabledDays[i]){
18003                     this.markInvalid(this.disabledDaysText);
18004                     return false;
18005                 }
18006             }
18007         }
18008         var fvalue = this.formatDate(value);
18009         if(this.ddMatch && this.ddMatch.test(fvalue)){
18010             this.markInvalid(String.format(this.disabledDatesText, fvalue));
18011             return false;
18012         }
18013         return true;
18014     },
18015
18016     // private
18017     // Provides logic to override the default TriggerField.validateBlur which just returns true
18018     validateBlur : function(){
18019         return !this.menu || !this.menu.isVisible();
18020     },
18021     
18022     getName: function()
18023     {
18024         // returns hidden if it's set..
18025         if (!this.rendered) {return ''};
18026         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
18027         
18028     },
18029
18030     /**
18031      * Returns the current date value of the date field.
18032      * @return {Date} The date value
18033      */
18034     getValue : function(){
18035         
18036         return  this.hiddenField ?
18037                 this.hiddenField.value :
18038                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
18039     },
18040
18041     /**
18042      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
18043      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
18044      * (the default format used is "m/d/y").
18045      * <br />Usage:
18046      * <pre><code>
18047 //All of these calls set the same date value (May 4, 2006)
18048
18049 //Pass a date object:
18050 var dt = new Date('5/4/06');
18051 dateField.setValue(dt);
18052
18053 //Pass a date string (default format):
18054 dateField.setValue('5/4/06');
18055
18056 //Pass a date string (custom format):
18057 dateField.format = 'Y-m-d';
18058 dateField.setValue('2006-5-4');
18059 </code></pre>
18060      * @param {String/Date} date The date or valid date string
18061      */
18062     setValue : function(date){
18063         if (this.hiddenField) {
18064             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
18065         }
18066         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
18067         // make sure the value field is always stored as a date..
18068         this.value = this.parseDate(date);
18069         
18070         
18071     },
18072
18073     // private
18074     parseDate : function(value){
18075         if(!value || value instanceof Date){
18076             return value;
18077         }
18078         var v = Date.parseDate(value, this.format);
18079          if (!v && this.useIso) {
18080             v = Date.parseDate(value, 'Y-m-d');
18081         }
18082         if(!v && this.altFormats){
18083             if(!this.altFormatsArray){
18084                 this.altFormatsArray = this.altFormats.split("|");
18085             }
18086             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18087                 v = Date.parseDate(value, this.altFormatsArray[i]);
18088             }
18089         }
18090         return v;
18091     },
18092
18093     // private
18094     formatDate : function(date, fmt){
18095         return (!date || !(date instanceof Date)) ?
18096                date : date.dateFormat(fmt || this.format);
18097     },
18098
18099     // private
18100     menuListeners : {
18101         select: function(m, d){
18102             
18103             this.setValue(d);
18104             this.fireEvent('select', this, d);
18105         },
18106         show : function(){ // retain focus styling
18107             this.onFocus();
18108         },
18109         hide : function(){
18110             this.focus.defer(10, this);
18111             var ml = this.menuListeners;
18112             this.menu.un("select", ml.select,  this);
18113             this.menu.un("show", ml.show,  this);
18114             this.menu.un("hide", ml.hide,  this);
18115         }
18116     },
18117
18118     // private
18119     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
18120     onTriggerClick : function(){
18121         if(this.disabled){
18122             return;
18123         }
18124         if(this.menu == null){
18125             this.menu = new Roo.menu.DateMenu();
18126         }
18127         Roo.apply(this.menu.picker,  {
18128             showClear: this.allowBlank,
18129             minDate : this.minValue,
18130             maxDate : this.maxValue,
18131             disabledDatesRE : this.ddMatch,
18132             disabledDatesText : this.disabledDatesText,
18133             disabledDays : this.disabledDays,
18134             disabledDaysText : this.disabledDaysText,
18135             format : this.useIso ? 'Y-m-d' : this.format,
18136             minText : String.format(this.minText, this.formatDate(this.minValue)),
18137             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
18138         });
18139         this.menu.on(Roo.apply({}, this.menuListeners, {
18140             scope:this
18141         }));
18142         this.menu.picker.setValue(this.getValue() || new Date());
18143         this.menu.show(this.el, "tl-bl?");
18144     },
18145
18146     beforeBlur : function(){
18147         var v = this.parseDate(this.getRawValue());
18148         if(v){
18149             this.setValue(v);
18150         }
18151     },
18152
18153     /*@
18154      * overide
18155      * 
18156      */
18157     isDirty : function() {
18158         if(this.disabled) {
18159             return false;
18160         }
18161         
18162         if(typeof(this.startValue) === 'undefined'){
18163             return false;
18164         }
18165         
18166         return String(this.getValue()) !== String(this.startValue);
18167         
18168     }
18169 });/*
18170  * Based on:
18171  * Ext JS Library 1.1.1
18172  * Copyright(c) 2006-2007, Ext JS, LLC.
18173  *
18174  * Originally Released Under LGPL - original licence link has changed is not relivant.
18175  *
18176  * Fork - LGPL
18177  * <script type="text/javascript">
18178  */
18179  
18180 /**
18181  * @class Roo.form.MonthField
18182  * @extends Roo.form.TriggerField
18183  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
18184 * @constructor
18185 * Create a new MonthField
18186 * @param {Object} config
18187  */
18188 Roo.form.MonthField = function(config){
18189     
18190     Roo.form.MonthField.superclass.constructor.call(this, config);
18191     
18192       this.addEvents({
18193          
18194         /**
18195          * @event select
18196          * Fires when a date is selected
18197              * @param {Roo.form.MonthFieeld} combo This combo box
18198              * @param {Date} date The date selected
18199              */
18200         'select' : true
18201          
18202     });
18203     
18204     
18205     if(typeof this.minValue == "string") {
18206         this.minValue = this.parseDate(this.minValue);
18207     }
18208     if(typeof this.maxValue == "string") {
18209         this.maxValue = this.parseDate(this.maxValue);
18210     }
18211     this.ddMatch = null;
18212     if(this.disabledDates){
18213         var dd = this.disabledDates;
18214         var re = "(?:";
18215         for(var i = 0; i < dd.length; i++){
18216             re += dd[i];
18217             if(i != dd.length-1) {
18218                 re += "|";
18219             }
18220         }
18221         this.ddMatch = new RegExp(re + ")");
18222     }
18223 };
18224
18225 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
18226     /**
18227      * @cfg {String} format
18228      * The default date format string which can be overriden for localization support.  The format must be
18229      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18230      */
18231     format : "M Y",
18232     /**
18233      * @cfg {String} altFormats
18234      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18235      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18236      */
18237     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
18238     /**
18239      * @cfg {Array} disabledDays
18240      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
18241      */
18242     disabledDays : [0,1,2,3,4,5,6],
18243     /**
18244      * @cfg {String} disabledDaysText
18245      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
18246      */
18247     disabledDaysText : "Disabled",
18248     /**
18249      * @cfg {Array} disabledDates
18250      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
18251      * expression so they are very powerful. Some examples:
18252      * <ul>
18253      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
18254      * <li>["03/08", "09/16"] would disable those days for every year</li>
18255      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
18256      * <li>["03/../2006"] would disable every day in March 2006</li>
18257      * <li>["^03"] would disable every day in every March</li>
18258      * </ul>
18259      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
18260      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
18261      */
18262     disabledDates : null,
18263     /**
18264      * @cfg {String} disabledDatesText
18265      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
18266      */
18267     disabledDatesText : "Disabled",
18268     /**
18269      * @cfg {Date/String} minValue
18270      * The minimum allowed date. Can be either a Javascript date object or a string date in a
18271      * valid format (defaults to null).
18272      */
18273     minValue : null,
18274     /**
18275      * @cfg {Date/String} maxValue
18276      * The maximum allowed date. Can be either a Javascript date object or a string date in a
18277      * valid format (defaults to null).
18278      */
18279     maxValue : null,
18280     /**
18281      * @cfg {String} minText
18282      * The error text to display when the date in the cell is before minValue (defaults to
18283      * 'The date in this field must be after {minValue}').
18284      */
18285     minText : "The date in this field must be equal to or after {0}",
18286     /**
18287      * @cfg {String} maxTextf
18288      * The error text to display when the date in the cell is after maxValue (defaults to
18289      * 'The date in this field must be before {maxValue}').
18290      */
18291     maxText : "The date in this field must be equal to or before {0}",
18292     /**
18293      * @cfg {String} invalidText
18294      * The error text to display when the date in the field is invalid (defaults to
18295      * '{value} is not a valid date - it must be in the format {format}').
18296      */
18297     invalidText : "{0} is not a valid date - it must be in the format {1}",
18298     /**
18299      * @cfg {String} triggerClass
18300      * An additional CSS class used to style the trigger button.  The trigger will always get the
18301      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
18302      * which displays a calendar icon).
18303      */
18304     triggerClass : 'x-form-date-trigger',
18305     
18306
18307     /**
18308      * @cfg {Boolean} useIso
18309      * if enabled, then the date field will use a hidden field to store the 
18310      * real value as iso formated date. default (true)
18311      */ 
18312     useIso : true,
18313     /**
18314      * @cfg {String/Object} autoCreate
18315      * A DomHelper element spec, or true for a default element spec (defaults to
18316      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
18317      */ 
18318     // private
18319     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
18320     
18321     // private
18322     hiddenField: false,
18323     
18324     hideMonthPicker : false,
18325     
18326     onRender : function(ct, position)
18327     {
18328         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
18329         if (this.useIso) {
18330             this.el.dom.removeAttribute('name'); 
18331             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
18332                     'before', true);
18333             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
18334             // prevent input submission
18335             this.hiddenName = this.name;
18336         }
18337             
18338             
18339     },
18340     
18341     // private
18342     validateValue : function(value)
18343     {
18344         value = this.formatDate(value);
18345         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
18346             return false;
18347         }
18348         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
18349              return true;
18350         }
18351         var svalue = value;
18352         value = this.parseDate(value);
18353         if(!value){
18354             this.markInvalid(String.format(this.invalidText, svalue, this.format));
18355             return false;
18356         }
18357         var time = value.getTime();
18358         if(this.minValue && time < this.minValue.getTime()){
18359             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
18360             return false;
18361         }
18362         if(this.maxValue && time > this.maxValue.getTime()){
18363             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
18364             return false;
18365         }
18366         /*if(this.disabledDays){
18367             var day = value.getDay();
18368             for(var i = 0; i < this.disabledDays.length; i++) {
18369                 if(day === this.disabledDays[i]){
18370                     this.markInvalid(this.disabledDaysText);
18371                     return false;
18372                 }
18373             }
18374         }
18375         */
18376         var fvalue = this.formatDate(value);
18377         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
18378             this.markInvalid(String.format(this.disabledDatesText, fvalue));
18379             return false;
18380         }
18381         */
18382         return true;
18383     },
18384
18385     // private
18386     // Provides logic to override the default TriggerField.validateBlur which just returns true
18387     validateBlur : function(){
18388         return !this.menu || !this.menu.isVisible();
18389     },
18390
18391     /**
18392      * Returns the current date value of the date field.
18393      * @return {Date} The date value
18394      */
18395     getValue : function(){
18396         
18397         
18398         
18399         return  this.hiddenField ?
18400                 this.hiddenField.value :
18401                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
18402     },
18403
18404     /**
18405      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
18406      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
18407      * (the default format used is "m/d/y").
18408      * <br />Usage:
18409      * <pre><code>
18410 //All of these calls set the same date value (May 4, 2006)
18411
18412 //Pass a date object:
18413 var dt = new Date('5/4/06');
18414 monthField.setValue(dt);
18415
18416 //Pass a date string (default format):
18417 monthField.setValue('5/4/06');
18418
18419 //Pass a date string (custom format):
18420 monthField.format = 'Y-m-d';
18421 monthField.setValue('2006-5-4');
18422 </code></pre>
18423      * @param {String/Date} date The date or valid date string
18424      */
18425     setValue : function(date){
18426         Roo.log('month setValue' + date);
18427         // can only be first of month..
18428         
18429         var val = this.parseDate(date);
18430         
18431         if (this.hiddenField) {
18432             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
18433         }
18434         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
18435         this.value = this.parseDate(date);
18436     },
18437
18438     // private
18439     parseDate : function(value){
18440         if(!value || value instanceof Date){
18441             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
18442             return value;
18443         }
18444         var v = Date.parseDate(value, this.format);
18445         if (!v && this.useIso) {
18446             v = Date.parseDate(value, 'Y-m-d');
18447         }
18448         if (v) {
18449             // 
18450             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
18451         }
18452         
18453         
18454         if(!v && this.altFormats){
18455             if(!this.altFormatsArray){
18456                 this.altFormatsArray = this.altFormats.split("|");
18457             }
18458             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18459                 v = Date.parseDate(value, this.altFormatsArray[i]);
18460             }
18461         }
18462         return v;
18463     },
18464
18465     // private
18466     formatDate : function(date, fmt){
18467         return (!date || !(date instanceof Date)) ?
18468                date : date.dateFormat(fmt || this.format);
18469     },
18470
18471     // private
18472     menuListeners : {
18473         select: function(m, d){
18474             this.setValue(d);
18475             this.fireEvent('select', this, d);
18476         },
18477         show : function(){ // retain focus styling
18478             this.onFocus();
18479         },
18480         hide : function(){
18481             this.focus.defer(10, this);
18482             var ml = this.menuListeners;
18483             this.menu.un("select", ml.select,  this);
18484             this.menu.un("show", ml.show,  this);
18485             this.menu.un("hide", ml.hide,  this);
18486         }
18487     },
18488     // private
18489     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
18490     onTriggerClick : function(){
18491         if(this.disabled){
18492             return;
18493         }
18494         if(this.menu == null){
18495             this.menu = new Roo.menu.DateMenu();
18496            
18497         }
18498         
18499         Roo.apply(this.menu.picker,  {
18500             
18501             showClear: this.allowBlank,
18502             minDate : this.minValue,
18503             maxDate : this.maxValue,
18504             disabledDatesRE : this.ddMatch,
18505             disabledDatesText : this.disabledDatesText,
18506             
18507             format : this.useIso ? 'Y-m-d' : this.format,
18508             minText : String.format(this.minText, this.formatDate(this.minValue)),
18509             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
18510             
18511         });
18512          this.menu.on(Roo.apply({}, this.menuListeners, {
18513             scope:this
18514         }));
18515        
18516         
18517         var m = this.menu;
18518         var p = m.picker;
18519         
18520         // hide month picker get's called when we called by 'before hide';
18521         
18522         var ignorehide = true;
18523         p.hideMonthPicker  = function(disableAnim){
18524             if (ignorehide) {
18525                 return;
18526             }
18527              if(this.monthPicker){
18528                 Roo.log("hideMonthPicker called");
18529                 if(disableAnim === true){
18530                     this.monthPicker.hide();
18531                 }else{
18532                     this.monthPicker.slideOut('t', {duration:.2});
18533                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
18534                     p.fireEvent("select", this, this.value);
18535                     m.hide();
18536                 }
18537             }
18538         }
18539         
18540         Roo.log('picker set value');
18541         Roo.log(this.getValue());
18542         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
18543         m.show(this.el, 'tl-bl?');
18544         ignorehide  = false;
18545         // this will trigger hideMonthPicker..
18546         
18547         
18548         // hidden the day picker
18549         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
18550         
18551         
18552         
18553       
18554         
18555         p.showMonthPicker.defer(100, p);
18556     
18557         
18558        
18559     },
18560
18561     beforeBlur : function(){
18562         var v = this.parseDate(this.getRawValue());
18563         if(v){
18564             this.setValue(v);
18565         }
18566     }
18567
18568     /** @cfg {Boolean} grow @hide */
18569     /** @cfg {Number} growMin @hide */
18570     /** @cfg {Number} growMax @hide */
18571     /**
18572      * @hide
18573      * @method autoSize
18574      */
18575 });/*
18576  * Based on:
18577  * Ext JS Library 1.1.1
18578  * Copyright(c) 2006-2007, Ext JS, LLC.
18579  *
18580  * Originally Released Under LGPL - original licence link has changed is not relivant.
18581  *
18582  * Fork - LGPL
18583  * <script type="text/javascript">
18584  */
18585  
18586
18587 /**
18588  * @class Roo.form.ComboBox
18589  * @extends Roo.form.TriggerField
18590  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
18591  * @constructor
18592  * Create a new ComboBox.
18593  * @param {Object} config Configuration options
18594  */
18595 Roo.form.ComboBox = function(config){
18596     Roo.form.ComboBox.superclass.constructor.call(this, config);
18597     this.addEvents({
18598         /**
18599          * @event expand
18600          * Fires when the dropdown list is expanded
18601              * @param {Roo.form.ComboBox} combo This combo box
18602              */
18603         'expand' : true,
18604         /**
18605          * @event collapse
18606          * Fires when the dropdown list is collapsed
18607              * @param {Roo.form.ComboBox} combo This combo box
18608              */
18609         'collapse' : true,
18610         /**
18611          * @event beforeselect
18612          * Fires before a list item is selected. Return false to cancel the selection.
18613              * @param {Roo.form.ComboBox} combo This combo box
18614              * @param {Roo.data.Record} record The data record returned from the underlying store
18615              * @param {Number} index The index of the selected item in the dropdown list
18616              */
18617         'beforeselect' : true,
18618         /**
18619          * @event select
18620          * Fires when a list item is selected
18621              * @param {Roo.form.ComboBox} combo This combo box
18622              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
18623              * @param {Number} index The index of the selected item in the dropdown list
18624              */
18625         'select' : true,
18626         /**
18627          * @event beforequery
18628          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
18629          * The event object passed has these properties:
18630              * @param {Roo.form.ComboBox} combo This combo box
18631              * @param {String} query The query
18632              * @param {Boolean} forceAll true to force "all" query
18633              * @param {Boolean} cancel true to cancel the query
18634              * @param {Object} e The query event object
18635              */
18636         'beforequery': true,
18637          /**
18638          * @event add
18639          * Fires when the 'add' icon is pressed (add a listener to enable add button)
18640              * @param {Roo.form.ComboBox} combo This combo box
18641              */
18642         'add' : true,
18643         /**
18644          * @event edit
18645          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
18646              * @param {Roo.form.ComboBox} combo This combo box
18647              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
18648              */
18649         'edit' : true
18650         
18651         
18652     });
18653     if(this.transform){
18654         this.allowDomMove = false;
18655         var s = Roo.getDom(this.transform);
18656         if(!this.hiddenName){
18657             this.hiddenName = s.name;
18658         }
18659         if(!this.store){
18660             this.mode = 'local';
18661             var d = [], opts = s.options;
18662             for(var i = 0, len = opts.length;i < len; i++){
18663                 var o = opts[i];
18664                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
18665                 if(o.selected) {
18666                     this.value = value;
18667                 }
18668                 d.push([value, o.text]);
18669             }
18670             this.store = new Roo.data.SimpleStore({
18671                 'id': 0,
18672                 fields: ['value', 'text'],
18673                 data : d
18674             });
18675             this.valueField = 'value';
18676             this.displayField = 'text';
18677         }
18678         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
18679         if(!this.lazyRender){
18680             this.target = true;
18681             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
18682             s.parentNode.removeChild(s); // remove it
18683             this.render(this.el.parentNode);
18684         }else{
18685             s.parentNode.removeChild(s); // remove it
18686         }
18687
18688     }
18689     if (this.store) {
18690         this.store = Roo.factory(this.store, Roo.data);
18691     }
18692     
18693     this.selectedIndex = -1;
18694     if(this.mode == 'local'){
18695         if(config.queryDelay === undefined){
18696             this.queryDelay = 10;
18697         }
18698         if(config.minChars === undefined){
18699             this.minChars = 0;
18700         }
18701     }
18702 };
18703
18704 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
18705     /**
18706      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
18707      */
18708     /**
18709      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
18710      * rendering into an Roo.Editor, defaults to false)
18711      */
18712     /**
18713      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
18714      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
18715      */
18716     /**
18717      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
18718      */
18719     /**
18720      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
18721      * the dropdown list (defaults to undefined, with no header element)
18722      */
18723
18724      /**
18725      * @cfg {String/Roo.Template} tpl The template to use to render the output
18726      */
18727      
18728     // private
18729     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
18730     /**
18731      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
18732      */
18733     listWidth: undefined,
18734     /**
18735      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
18736      * mode = 'remote' or 'text' if mode = 'local')
18737      */
18738     displayField: undefined,
18739     /**
18740      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
18741      * mode = 'remote' or 'value' if mode = 'local'). 
18742      * Note: use of a valueField requires the user make a selection
18743      * in order for a value to be mapped.
18744      */
18745     valueField: undefined,
18746     
18747     
18748     /**
18749      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
18750      * field's data value (defaults to the underlying DOM element's name)
18751      */
18752     hiddenName: undefined,
18753     /**
18754      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
18755      */
18756     listClass: '',
18757     /**
18758      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
18759      */
18760     selectedClass: 'x-combo-selected',
18761     /**
18762      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
18763      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
18764      * which displays a downward arrow icon).
18765      */
18766     triggerClass : 'x-form-arrow-trigger',
18767     /**
18768      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
18769      */
18770     shadow:'sides',
18771     /**
18772      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
18773      * anchor positions (defaults to 'tl-bl')
18774      */
18775     listAlign: 'tl-bl?',
18776     /**
18777      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
18778      */
18779     maxHeight: 300,
18780     /**
18781      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
18782      * query specified by the allQuery config option (defaults to 'query')
18783      */
18784     triggerAction: 'query',
18785     /**
18786      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
18787      * (defaults to 4, does not apply if editable = false)
18788      */
18789     minChars : 4,
18790     /**
18791      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
18792      * delay (typeAheadDelay) if it matches a known value (defaults to false)
18793      */
18794     typeAhead: false,
18795     /**
18796      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
18797      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
18798      */
18799     queryDelay: 500,
18800     /**
18801      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
18802      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
18803      */
18804     pageSize: 0,
18805     /**
18806      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
18807      * when editable = true (defaults to false)
18808      */
18809     selectOnFocus:false,
18810     /**
18811      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
18812      */
18813     queryParam: 'query',
18814     /**
18815      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
18816      * when mode = 'remote' (defaults to 'Loading...')
18817      */
18818     loadingText: 'Loading...',
18819     /**
18820      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
18821      */
18822     resizable: false,
18823     /**
18824      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
18825      */
18826     handleHeight : 8,
18827     /**
18828      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
18829      * traditional select (defaults to true)
18830      */
18831     editable: true,
18832     /**
18833      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
18834      */
18835     allQuery: '',
18836     /**
18837      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
18838      */
18839     mode: 'remote',
18840     /**
18841      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
18842      * listWidth has a higher value)
18843      */
18844     minListWidth : 70,
18845     /**
18846      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
18847      * allow the user to set arbitrary text into the field (defaults to false)
18848      */
18849     forceSelection:false,
18850     /**
18851      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
18852      * if typeAhead = true (defaults to 250)
18853      */
18854     typeAheadDelay : 250,
18855     /**
18856      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
18857      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
18858      */
18859     valueNotFoundText : undefined,
18860     /**
18861      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
18862      */
18863     blockFocus : false,
18864     
18865     /**
18866      * @cfg {Boolean} disableClear Disable showing of clear button.
18867      */
18868     disableClear : false,
18869     /**
18870      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
18871      */
18872     alwaysQuery : false,
18873     
18874     //private
18875     addicon : false,
18876     editicon: false,
18877     
18878     // element that contains real text value.. (when hidden is used..)
18879      
18880     // private
18881     onRender : function(ct, position){
18882         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
18883         if(this.hiddenName){
18884             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
18885                     'before', true);
18886             this.hiddenField.value =
18887                 this.hiddenValue !== undefined ? this.hiddenValue :
18888                 this.value !== undefined ? this.value : '';
18889
18890             // prevent input submission
18891             this.el.dom.removeAttribute('name');
18892              
18893              
18894         }
18895         if(Roo.isGecko){
18896             this.el.dom.setAttribute('autocomplete', 'off');
18897         }
18898
18899         var cls = 'x-combo-list';
18900
18901         this.list = new Roo.Layer({
18902             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
18903         });
18904
18905         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
18906         this.list.setWidth(lw);
18907         this.list.swallowEvent('mousewheel');
18908         this.assetHeight = 0;
18909
18910         if(this.title){
18911             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
18912             this.assetHeight += this.header.getHeight();
18913         }
18914
18915         this.innerList = this.list.createChild({cls:cls+'-inner'});
18916         this.innerList.on('mouseover', this.onViewOver, this);
18917         this.innerList.on('mousemove', this.onViewMove, this);
18918         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18919         
18920         if(this.allowBlank && !this.pageSize && !this.disableClear){
18921             this.footer = this.list.createChild({cls:cls+'-ft'});
18922             this.pageTb = new Roo.Toolbar(this.footer);
18923            
18924         }
18925         if(this.pageSize){
18926             this.footer = this.list.createChild({cls:cls+'-ft'});
18927             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
18928                     {pageSize: this.pageSize});
18929             
18930         }
18931         
18932         if (this.pageTb && this.allowBlank && !this.disableClear) {
18933             var _this = this;
18934             this.pageTb.add(new Roo.Toolbar.Fill(), {
18935                 cls: 'x-btn-icon x-btn-clear',
18936                 text: '&#160;',
18937                 handler: function()
18938                 {
18939                     _this.collapse();
18940                     _this.clearValue();
18941                     _this.onSelect(false, -1);
18942                 }
18943             });
18944         }
18945         if (this.footer) {
18946             this.assetHeight += this.footer.getHeight();
18947         }
18948         
18949
18950         if(!this.tpl){
18951             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
18952         }
18953
18954         this.view = new Roo.View(this.innerList, this.tpl, {
18955             singleSelect:true, store: this.store, selectedClass: this.selectedClass
18956         });
18957
18958         this.view.on('click', this.onViewClick, this);
18959
18960         this.store.on('beforeload', this.onBeforeLoad, this);
18961         this.store.on('load', this.onLoad, this);
18962         this.store.on('loadexception', this.onLoadException, this);
18963
18964         if(this.resizable){
18965             this.resizer = new Roo.Resizable(this.list,  {
18966                pinned:true, handles:'se'
18967             });
18968             this.resizer.on('resize', function(r, w, h){
18969                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
18970                 this.listWidth = w;
18971                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
18972                 this.restrictHeight();
18973             }, this);
18974             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
18975         }
18976         if(!this.editable){
18977             this.editable = true;
18978             this.setEditable(false);
18979         }  
18980         
18981         
18982         if (typeof(this.events.add.listeners) != 'undefined') {
18983             
18984             this.addicon = this.wrap.createChild(
18985                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
18986        
18987             this.addicon.on('click', function(e) {
18988                 this.fireEvent('add', this);
18989             }, this);
18990         }
18991         if (typeof(this.events.edit.listeners) != 'undefined') {
18992             
18993             this.editicon = this.wrap.createChild(
18994                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
18995             if (this.addicon) {
18996                 this.editicon.setStyle('margin-left', '40px');
18997             }
18998             this.editicon.on('click', function(e) {
18999                 
19000                 // we fire even  if inothing is selected..
19001                 this.fireEvent('edit', this, this.lastData );
19002                 
19003             }, this);
19004         }
19005         
19006         
19007         
19008     },
19009
19010     // private
19011     initEvents : function(){
19012         Roo.form.ComboBox.superclass.initEvents.call(this);
19013
19014         this.keyNav = new Roo.KeyNav(this.el, {
19015             "up" : function(e){
19016                 this.inKeyMode = true;
19017                 this.selectPrev();
19018             },
19019
19020             "down" : function(e){
19021                 if(!this.isExpanded()){
19022                     this.onTriggerClick();
19023                 }else{
19024                     this.inKeyMode = true;
19025                     this.selectNext();
19026                 }
19027             },
19028
19029             "enter" : function(e){
19030                 this.onViewClick();
19031                 //return true;
19032             },
19033
19034             "esc" : function(e){
19035                 this.collapse();
19036             },
19037
19038             "tab" : function(e){
19039                 this.onViewClick(false);
19040                 this.fireEvent("specialkey", this, e);
19041                 return true;
19042             },
19043
19044             scope : this,
19045
19046             doRelay : function(foo, bar, hname){
19047                 if(hname == 'down' || this.scope.isExpanded()){
19048                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
19049                 }
19050                 return true;
19051             },
19052
19053             forceKeyDown: true
19054         });
19055         this.queryDelay = Math.max(this.queryDelay || 10,
19056                 this.mode == 'local' ? 10 : 250);
19057         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
19058         if(this.typeAhead){
19059             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
19060         }
19061         if(this.editable !== false){
19062             this.el.on("keyup", this.onKeyUp, this);
19063         }
19064         if(this.forceSelection){
19065             this.on('blur', this.doForce, this);
19066         }
19067     },
19068
19069     onDestroy : function(){
19070         if(this.view){
19071             this.view.setStore(null);
19072             this.view.el.removeAllListeners();
19073             this.view.el.remove();
19074             this.view.purgeListeners();
19075         }
19076         if(this.list){
19077             this.list.destroy();
19078         }
19079         if(this.store){
19080             this.store.un('beforeload', this.onBeforeLoad, this);
19081             this.store.un('load', this.onLoad, this);
19082             this.store.un('loadexception', this.onLoadException, this);
19083         }
19084         Roo.form.ComboBox.superclass.onDestroy.call(this);
19085     },
19086
19087     // private
19088     fireKey : function(e){
19089         if(e.isNavKeyPress() && !this.list.isVisible()){
19090             this.fireEvent("specialkey", this, e);
19091         }
19092     },
19093
19094     // private
19095     onResize: function(w, h){
19096         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
19097         
19098         if(typeof w != 'number'){
19099             // we do not handle it!?!?
19100             return;
19101         }
19102         var tw = this.trigger.getWidth();
19103         tw += this.addicon ? this.addicon.getWidth() : 0;
19104         tw += this.editicon ? this.editicon.getWidth() : 0;
19105         var x = w - tw;
19106         this.el.setWidth( this.adjustWidth('input', x));
19107             
19108         this.trigger.setStyle('left', x+'px');
19109         
19110         if(this.list && this.listWidth === undefined){
19111             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
19112             this.list.setWidth(lw);
19113             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19114         }
19115         
19116     
19117         
19118     },
19119
19120     /**
19121      * Allow or prevent the user from directly editing the field text.  If false is passed,
19122      * the user will only be able to select from the items defined in the dropdown list.  This method
19123      * is the runtime equivalent of setting the 'editable' config option at config time.
19124      * @param {Boolean} value True to allow the user to directly edit the field text
19125      */
19126     setEditable : function(value){
19127         if(value == this.editable){
19128             return;
19129         }
19130         this.editable = value;
19131         if(!value){
19132             this.el.dom.setAttribute('readOnly', true);
19133             this.el.on('mousedown', this.onTriggerClick,  this);
19134             this.el.addClass('x-combo-noedit');
19135         }else{
19136             this.el.dom.setAttribute('readOnly', false);
19137             this.el.un('mousedown', this.onTriggerClick,  this);
19138             this.el.removeClass('x-combo-noedit');
19139         }
19140     },
19141
19142     // private
19143     onBeforeLoad : function(){
19144         if(!this.hasFocus){
19145             return;
19146         }
19147         this.innerList.update(this.loadingText ?
19148                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
19149         this.restrictHeight();
19150         this.selectedIndex = -1;
19151     },
19152
19153     // private
19154     onLoad : function(){
19155         if(!this.hasFocus){
19156             return;
19157         }
19158         if(this.store.getCount() > 0){
19159             this.expand();
19160             this.restrictHeight();
19161             if(this.lastQuery == this.allQuery){
19162                 if(this.editable){
19163                     this.el.dom.select();
19164                 }
19165                 if(!this.selectByValue(this.value, true)){
19166                     this.select(0, true);
19167                 }
19168             }else{
19169                 this.selectNext();
19170                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
19171                     this.taTask.delay(this.typeAheadDelay);
19172                 }
19173             }
19174         }else{
19175             this.onEmptyResults();
19176         }
19177         //this.el.focus();
19178     },
19179     // private
19180     onLoadException : function()
19181     {
19182         this.collapse();
19183         Roo.log(this.store.reader.jsonData);
19184         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
19185             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
19186         }
19187         
19188         
19189     },
19190     // private
19191     onTypeAhead : function(){
19192         if(this.store.getCount() > 0){
19193             var r = this.store.getAt(0);
19194             var newValue = r.data[this.displayField];
19195             var len = newValue.length;
19196             var selStart = this.getRawValue().length;
19197             if(selStart != len){
19198                 this.setRawValue(newValue);
19199                 this.selectText(selStart, newValue.length);
19200             }
19201         }
19202     },
19203
19204     // private
19205     onSelect : function(record, index){
19206         if(this.fireEvent('beforeselect', this, record, index) !== false){
19207             this.setFromData(index > -1 ? record.data : false);
19208             this.collapse();
19209             this.fireEvent('select', this, record, index);
19210         }
19211     },
19212
19213     /**
19214      * Returns the currently selected field value or empty string if no value is set.
19215      * @return {String} value The selected value
19216      */
19217     getValue : function(){
19218         if(this.valueField){
19219             return typeof this.value != 'undefined' ? this.value : '';
19220         }
19221         return Roo.form.ComboBox.superclass.getValue.call(this);
19222     },
19223
19224     /**
19225      * Clears any text/value currently set in the field
19226      */
19227     clearValue : function(){
19228         if(this.hiddenField){
19229             this.hiddenField.value = '';
19230         }
19231         this.value = '';
19232         this.setRawValue('');
19233         this.lastSelectionText = '';
19234         
19235     },
19236
19237     /**
19238      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
19239      * will be displayed in the field.  If the value does not match the data value of an existing item,
19240      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
19241      * Otherwise the field will be blank (although the value will still be set).
19242      * @param {String} value The value to match
19243      */
19244     setValue : function(v){
19245         var text = v;
19246         if(this.valueField){
19247             var r = this.findRecord(this.valueField, v);
19248             if(r){
19249                 text = r.data[this.displayField];
19250             }else if(this.valueNotFoundText !== undefined){
19251                 text = this.valueNotFoundText;
19252             }
19253         }
19254         this.lastSelectionText = text;
19255         if(this.hiddenField){
19256             this.hiddenField.value = v;
19257         }
19258         Roo.form.ComboBox.superclass.setValue.call(this, text);
19259         this.value = v;
19260     },
19261     /**
19262      * @property {Object} the last set data for the element
19263      */
19264     
19265     lastData : false,
19266     /**
19267      * Sets the value of the field based on a object which is related to the record format for the store.
19268      * @param {Object} value the value to set as. or false on reset?
19269      */
19270     setFromData : function(o){
19271         var dv = ''; // display value
19272         var vv = ''; // value value..
19273         this.lastData = o;
19274         if (this.displayField) {
19275             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
19276         } else {
19277             // this is an error condition!!!
19278             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
19279         }
19280         
19281         if(this.valueField){
19282             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
19283         }
19284         if(this.hiddenField){
19285             this.hiddenField.value = vv;
19286             
19287             this.lastSelectionText = dv;
19288             Roo.form.ComboBox.superclass.setValue.call(this, dv);
19289             this.value = vv;
19290             return;
19291         }
19292         // no hidden field.. - we store the value in 'value', but still display
19293         // display field!!!!
19294         this.lastSelectionText = dv;
19295         Roo.form.ComboBox.superclass.setValue.call(this, dv);
19296         this.value = vv;
19297         
19298         
19299     },
19300     // private
19301     reset : function(){
19302         // overridden so that last data is reset..
19303         this.setValue(this.resetValue);
19304         this.originalValue = this.getValue();
19305         this.clearInvalid();
19306         this.lastData = false;
19307         if (this.view) {
19308             this.view.clearSelections();
19309         }
19310     },
19311     // private
19312     findRecord : function(prop, value){
19313         var record;
19314         if(this.store.getCount() > 0){
19315             this.store.each(function(r){
19316                 if(r.data[prop] == value){
19317                     record = r;
19318                     return false;
19319                 }
19320                 return true;
19321             });
19322         }
19323         return record;
19324     },
19325     
19326     getName: function()
19327     {
19328         // returns hidden if it's set..
19329         if (!this.rendered) {return ''};
19330         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
19331         
19332     },
19333     // private
19334     onViewMove : function(e, t){
19335         this.inKeyMode = false;
19336     },
19337
19338     // private
19339     onViewOver : function(e, t){
19340         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
19341             return;
19342         }
19343         var item = this.view.findItemFromChild(t);
19344         if(item){
19345             var index = this.view.indexOf(item);
19346             this.select(index, false);
19347         }
19348     },
19349
19350     // private
19351     onViewClick : function(doFocus)
19352     {
19353         var index = this.view.getSelectedIndexes()[0];
19354         var r = this.store.getAt(index);
19355         if(r){
19356             this.onSelect(r, index);
19357         }
19358         if(doFocus !== false && !this.blockFocus){
19359             this.el.focus();
19360         }
19361     },
19362
19363     // private
19364     restrictHeight : function(){
19365         this.innerList.dom.style.height = '';
19366         var inner = this.innerList.dom;
19367         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
19368         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
19369         this.list.beginUpdate();
19370         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
19371         this.list.alignTo(this.el, this.listAlign);
19372         this.list.endUpdate();
19373     },
19374
19375     // private
19376     onEmptyResults : function(){
19377         this.collapse();
19378     },
19379
19380     /**
19381      * Returns true if the dropdown list is expanded, else false.
19382      */
19383     isExpanded : function(){
19384         return this.list.isVisible();
19385     },
19386
19387     /**
19388      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
19389      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19390      * @param {String} value The data value of the item to select
19391      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19392      * selected item if it is not currently in view (defaults to true)
19393      * @return {Boolean} True if the value matched an item in the list, else false
19394      */
19395     selectByValue : function(v, scrollIntoView){
19396         if(v !== undefined && v !== null){
19397             var r = this.findRecord(this.valueField || this.displayField, v);
19398             if(r){
19399                 this.select(this.store.indexOf(r), scrollIntoView);
19400                 return true;
19401             }
19402         }
19403         return false;
19404     },
19405
19406     /**
19407      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
19408      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19409      * @param {Number} index The zero-based index of the list item to select
19410      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19411      * selected item if it is not currently in view (defaults to true)
19412      */
19413     select : function(index, scrollIntoView){
19414         this.selectedIndex = index;
19415         this.view.select(index);
19416         if(scrollIntoView !== false){
19417             var el = this.view.getNode(index);
19418             if(el){
19419                 this.innerList.scrollChildIntoView(el, false);
19420             }
19421         }
19422     },
19423
19424     // private
19425     selectNext : function(){
19426         var ct = this.store.getCount();
19427         if(ct > 0){
19428             if(this.selectedIndex == -1){
19429                 this.select(0);
19430             }else if(this.selectedIndex < ct-1){
19431                 this.select(this.selectedIndex+1);
19432             }
19433         }
19434     },
19435
19436     // private
19437     selectPrev : function(){
19438         var ct = this.store.getCount();
19439         if(ct > 0){
19440             if(this.selectedIndex == -1){
19441                 this.select(0);
19442             }else if(this.selectedIndex != 0){
19443                 this.select(this.selectedIndex-1);
19444             }
19445         }
19446     },
19447
19448     // private
19449     onKeyUp : function(e){
19450         if(this.editable !== false && !e.isSpecialKey()){
19451             this.lastKey = e.getKey();
19452             this.dqTask.delay(this.queryDelay);
19453         }
19454     },
19455
19456     // private
19457     validateBlur : function(){
19458         return !this.list || !this.list.isVisible();   
19459     },
19460
19461     // private
19462     initQuery : function(){
19463         this.doQuery(this.getRawValue());
19464     },
19465
19466     // private
19467     doForce : function(){
19468         if(this.el.dom.value.length > 0){
19469             this.el.dom.value =
19470                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
19471              
19472         }
19473     },
19474
19475     /**
19476      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
19477      * query allowing the query action to be canceled if needed.
19478      * @param {String} query The SQL query to execute
19479      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
19480      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
19481      * saved in the current store (defaults to false)
19482      */
19483     doQuery : function(q, forceAll){
19484         if(q === undefined || q === null){
19485             q = '';
19486         }
19487         var qe = {
19488             query: q,
19489             forceAll: forceAll,
19490             combo: this,
19491             cancel:false
19492         };
19493         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
19494             return false;
19495         }
19496         q = qe.query;
19497         forceAll = qe.forceAll;
19498         if(forceAll === true || (q.length >= this.minChars)){
19499             if(this.lastQuery != q || this.alwaysQuery){
19500                 this.lastQuery = q;
19501                 if(this.mode == 'local'){
19502                     this.selectedIndex = -1;
19503                     if(forceAll){
19504                         this.store.clearFilter();
19505                     }else{
19506                         this.store.filter(this.displayField, q);
19507                     }
19508                     this.onLoad();
19509                 }else{
19510                     this.store.baseParams[this.queryParam] = q;
19511                     this.store.load({
19512                         params: this.getParams(q)
19513                     });
19514                     this.expand();
19515                 }
19516             }else{
19517                 this.selectedIndex = -1;
19518                 this.onLoad();   
19519             }
19520         }
19521     },
19522
19523     // private
19524     getParams : function(q){
19525         var p = {};
19526         //p[this.queryParam] = q;
19527         if(this.pageSize){
19528             p.start = 0;
19529             p.limit = this.pageSize;
19530         }
19531         return p;
19532     },
19533
19534     /**
19535      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
19536      */
19537     collapse : function(){
19538         if(!this.isExpanded()){
19539             return;
19540         }
19541         this.list.hide();
19542         Roo.get(document).un('mousedown', this.collapseIf, this);
19543         Roo.get(document).un('mousewheel', this.collapseIf, this);
19544         if (!this.editable) {
19545             Roo.get(document).un('keydown', this.listKeyPress, this);
19546         }
19547         this.fireEvent('collapse', this);
19548     },
19549
19550     // private
19551     collapseIf : function(e){
19552         if(!e.within(this.wrap) && !e.within(this.list)){
19553             this.collapse();
19554         }
19555     },
19556
19557     /**
19558      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
19559      */
19560     expand : function(){
19561         if(this.isExpanded() || !this.hasFocus){
19562             return;
19563         }
19564         this.list.alignTo(this.el, this.listAlign);
19565         this.list.show();
19566         Roo.get(document).on('mousedown', this.collapseIf, this);
19567         Roo.get(document).on('mousewheel', this.collapseIf, this);
19568         if (!this.editable) {
19569             Roo.get(document).on('keydown', this.listKeyPress, this);
19570         }
19571         
19572         this.fireEvent('expand', this);
19573     },
19574
19575     // private
19576     // Implements the default empty TriggerField.onTriggerClick function
19577     onTriggerClick : function(){
19578         if(this.disabled){
19579             return;
19580         }
19581         if(this.isExpanded()){
19582             this.collapse();
19583             if (!this.blockFocus) {
19584                 this.el.focus();
19585             }
19586             
19587         }else {
19588             this.hasFocus = true;
19589             if(this.triggerAction == 'all') {
19590                 this.doQuery(this.allQuery, true);
19591             } else {
19592                 this.doQuery(this.getRawValue());
19593             }
19594             if (!this.blockFocus) {
19595                 this.el.focus();
19596             }
19597         }
19598     },
19599     listKeyPress : function(e)
19600     {
19601         //Roo.log('listkeypress');
19602         // scroll to first matching element based on key pres..
19603         if (e.isSpecialKey()) {
19604             return false;
19605         }
19606         var k = String.fromCharCode(e.getKey()).toUpperCase();
19607         //Roo.log(k);
19608         var match  = false;
19609         var csel = this.view.getSelectedNodes();
19610         var cselitem = false;
19611         if (csel.length) {
19612             var ix = this.view.indexOf(csel[0]);
19613             cselitem  = this.store.getAt(ix);
19614             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
19615                 cselitem = false;
19616             }
19617             
19618         }
19619         
19620         this.store.each(function(v) { 
19621             if (cselitem) {
19622                 // start at existing selection.
19623                 if (cselitem.id == v.id) {
19624                     cselitem = false;
19625                 }
19626                 return;
19627             }
19628                 
19629             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
19630                 match = this.store.indexOf(v);
19631                 return false;
19632             }
19633         }, this);
19634         
19635         if (match === false) {
19636             return true; // no more action?
19637         }
19638         // scroll to?
19639         this.view.select(match);
19640         var sn = Roo.get(this.view.getSelectedNodes()[0]);
19641         sn.scrollIntoView(sn.dom.parentNode, false);
19642     }
19643
19644     /** 
19645     * @cfg {Boolean} grow 
19646     * @hide 
19647     */
19648     /** 
19649     * @cfg {Number} growMin 
19650     * @hide 
19651     */
19652     /** 
19653     * @cfg {Number} growMax 
19654     * @hide 
19655     */
19656     /**
19657      * @hide
19658      * @method autoSize
19659      */
19660 });/*
19661  * Copyright(c) 2010-2012, Roo J Solutions Limited
19662  *
19663  * Licence LGPL
19664  *
19665  */
19666
19667 /**
19668  * @class Roo.form.ComboBoxArray
19669  * @extends Roo.form.TextField
19670  * A facebook style adder... for lists of email / people / countries  etc...
19671  * pick multiple items from a combo box, and shows each one.
19672  *
19673  *  Fred [x]  Brian [x]  [Pick another |v]
19674  *
19675  *
19676  *  For this to work: it needs various extra information
19677  *    - normal combo problay has
19678  *      name, hiddenName
19679  *    + displayField, valueField
19680  *
19681  *    For our purpose...
19682  *
19683  *
19684  *   If we change from 'extends' to wrapping...
19685  *   
19686  *  
19687  *
19688  
19689  
19690  * @constructor
19691  * Create a new ComboBoxArray.
19692  * @param {Object} config Configuration options
19693  */
19694  
19695
19696 Roo.form.ComboBoxArray = function(config)
19697 {
19698     this.addEvents({
19699         /**
19700          * @event beforeremove
19701          * Fires before remove the value from the list
19702              * @param {Roo.form.ComboBoxArray} _self This combo box array
19703              * @param {Roo.form.ComboBoxArray.Item} item removed item
19704              */
19705         'beforeremove' : true,
19706         /**
19707          * @event remove
19708          * Fires when remove the value from the list
19709              * @param {Roo.form.ComboBoxArray} _self This combo box array
19710              * @param {Roo.form.ComboBoxArray.Item} item removed item
19711              */
19712         'remove' : true
19713         
19714         
19715     });
19716     
19717     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
19718     
19719     this.items = new Roo.util.MixedCollection(false);
19720     
19721     // construct the child combo...
19722     
19723     
19724     
19725     
19726    
19727     
19728 }
19729
19730  
19731 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
19732
19733     /**
19734      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
19735      */
19736     
19737     lastData : false,
19738     
19739     // behavies liek a hiddne field
19740     inputType:      'hidden',
19741     /**
19742      * @cfg {Number} width The width of the box that displays the selected element
19743      */ 
19744     width:          300,
19745
19746     
19747     
19748     /**
19749      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
19750      */
19751     name : false,
19752     /**
19753      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
19754      */
19755     hiddenName : false,
19756     
19757     
19758     // private the array of items that are displayed..
19759     items  : false,
19760     // private - the hidden field el.
19761     hiddenEl : false,
19762     // private - the filed el..
19763     el : false,
19764     
19765     //validateValue : function() { return true; }, // all values are ok!
19766     //onAddClick: function() { },
19767     
19768     onRender : function(ct, position) 
19769     {
19770         
19771         // create the standard hidden element
19772         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
19773         
19774         
19775         // give fake names to child combo;
19776         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
19777         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
19778         
19779         this.combo = Roo.factory(this.combo, Roo.form);
19780         this.combo.onRender(ct, position);
19781         if (typeof(this.combo.width) != 'undefined') {
19782             this.combo.onResize(this.combo.width,0);
19783         }
19784         
19785         this.combo.initEvents();
19786         
19787         // assigned so form know we need to do this..
19788         this.store          = this.combo.store;
19789         this.valueField     = this.combo.valueField;
19790         this.displayField   = this.combo.displayField ;
19791         
19792         
19793         this.combo.wrap.addClass('x-cbarray-grp');
19794         
19795         var cbwrap = this.combo.wrap.createChild(
19796             {tag: 'div', cls: 'x-cbarray-cb'},
19797             this.combo.el.dom
19798         );
19799         
19800              
19801         this.hiddenEl = this.combo.wrap.createChild({
19802             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
19803         });
19804         this.el = this.combo.wrap.createChild({
19805             tag: 'input',  type:'hidden' , name: this.name, value : ''
19806         });
19807          //   this.el.dom.removeAttribute("name");
19808         
19809         
19810         this.outerWrap = this.combo.wrap;
19811         this.wrap = cbwrap;
19812         
19813         this.outerWrap.setWidth(this.width);
19814         this.outerWrap.dom.removeChild(this.el.dom);
19815         
19816         this.wrap.dom.appendChild(this.el.dom);
19817         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
19818         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
19819         
19820         this.combo.trigger.setStyle('position','relative');
19821         this.combo.trigger.setStyle('left', '0px');
19822         this.combo.trigger.setStyle('top', '2px');
19823         
19824         this.combo.el.setStyle('vertical-align', 'text-bottom');
19825         
19826         //this.trigger.setStyle('vertical-align', 'top');
19827         
19828         // this should use the code from combo really... on('add' ....)
19829         if (this.adder) {
19830             
19831         
19832             this.adder = this.outerWrap.createChild(
19833                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
19834             var _t = this;
19835             this.adder.on('click', function(e) {
19836                 _t.fireEvent('adderclick', this, e);
19837             }, _t);
19838         }
19839         //var _t = this;
19840         //this.adder.on('click', this.onAddClick, _t);
19841         
19842         
19843         this.combo.on('select', function(cb, rec, ix) {
19844             this.addItem(rec.data);
19845             
19846             cb.setValue('');
19847             cb.el.dom.value = '';
19848             //cb.lastData = rec.data;
19849             // add to list
19850             
19851         }, this);
19852         
19853         
19854     },
19855     
19856     
19857     getName: function()
19858     {
19859         // returns hidden if it's set..
19860         if (!this.rendered) {return ''};
19861         return  this.hiddenName ? this.hiddenName : this.name;
19862         
19863     },
19864     
19865     
19866     onResize: function(w, h){
19867         
19868         return;
19869         // not sure if this is needed..
19870         //this.combo.onResize(w,h);
19871         
19872         if(typeof w != 'number'){
19873             // we do not handle it!?!?
19874             return;
19875         }
19876         var tw = this.combo.trigger.getWidth();
19877         tw += this.addicon ? this.addicon.getWidth() : 0;
19878         tw += this.editicon ? this.editicon.getWidth() : 0;
19879         var x = w - tw;
19880         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
19881             
19882         this.combo.trigger.setStyle('left', '0px');
19883         
19884         if(this.list && this.listWidth === undefined){
19885             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
19886             this.list.setWidth(lw);
19887             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19888         }
19889         
19890     
19891         
19892     },
19893     
19894     addItem: function(rec)
19895     {
19896         var valueField = this.combo.valueField;
19897         var displayField = this.combo.displayField;
19898         if (this.items.indexOfKey(rec[valueField]) > -1) {
19899             //console.log("GOT " + rec.data.id);
19900             return;
19901         }
19902         
19903         var x = new Roo.form.ComboBoxArray.Item({
19904             //id : rec[this.idField],
19905             data : rec,
19906             displayField : displayField ,
19907             tipField : displayField ,
19908             cb : this
19909         });
19910         // use the 
19911         this.items.add(rec[valueField],x);
19912         // add it before the element..
19913         this.updateHiddenEl();
19914         x.render(this.outerWrap, this.wrap.dom);
19915         // add the image handler..
19916     },
19917     
19918     updateHiddenEl : function()
19919     {
19920         this.validate();
19921         if (!this.hiddenEl) {
19922             return;
19923         }
19924         var ar = [];
19925         var idField = this.combo.valueField;
19926         
19927         this.items.each(function(f) {
19928             ar.push(f.data[idField]);
19929            
19930         });
19931         this.hiddenEl.dom.value = ar.join(',');
19932         this.validate();
19933     },
19934     
19935     reset : function()
19936     {
19937         this.items.clear();
19938         
19939         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
19940            el.remove();
19941         });
19942         
19943         this.el.dom.value = '';
19944         if (this.hiddenEl) {
19945             this.hiddenEl.dom.value = '';
19946         }
19947         
19948     },
19949     getValue: function()
19950     {
19951         return this.hiddenEl ? this.hiddenEl.dom.value : '';
19952     },
19953     setValue: function(v) // not a valid action - must use addItems..
19954     {
19955          
19956         this.reset();
19957         
19958         
19959         
19960         if (this.store.isLocal && (typeof(v) == 'string')) {
19961             // then we can use the store to find the values..
19962             // comma seperated at present.. this needs to allow JSON based encoding..
19963             this.hiddenEl.value  = v;
19964             var v_ar = [];
19965             Roo.each(v.split(','), function(k) {
19966                 Roo.log("CHECK " + this.valueField + ',' + k);
19967                 var li = this.store.query(this.valueField, k);
19968                 if (!li.length) {
19969                     return;
19970                 }
19971                 var add = {};
19972                 add[this.valueField] = k;
19973                 add[this.displayField] = li.item(0).data[this.displayField];
19974                 
19975                 this.addItem(add);
19976             }, this) 
19977              
19978         }
19979         if (typeof(v) == 'object' ) {
19980             // then let's assume it's an array of objects..
19981             Roo.each(v, function(l) {
19982                 this.addItem(l);
19983             }, this);
19984              
19985         }
19986         
19987         
19988     },
19989     setFromData: function(v)
19990     {
19991         // this recieves an object, if setValues is called.
19992         this.reset();
19993         this.el.dom.value = v[this.displayField];
19994         this.hiddenEl.dom.value = v[this.valueField];
19995         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
19996             return;
19997         }
19998         var kv = v[this.valueField];
19999         var dv = v[this.displayField];
20000         kv = typeof(kv) != 'string' ? '' : kv;
20001         dv = typeof(dv) != 'string' ? '' : dv;
20002         
20003         
20004         var keys = kv.split(',');
20005         var display = dv.split(',');
20006         for (var i = 0 ; i < keys.length; i++) {
20007             
20008             add = {};
20009             add[this.valueField] = keys[i];
20010             add[this.displayField] = display[i];
20011             this.addItem(add);
20012         }
20013       
20014         
20015     },
20016     
20017     /**
20018      * Validates the combox array value
20019      * @return {Boolean} True if the value is valid, else false
20020      */
20021     validate : function(){
20022         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
20023             this.clearInvalid();
20024             return true;
20025         }
20026         return false;
20027     },
20028     
20029     validateValue : function(value){
20030         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
20031         
20032     },
20033     
20034     /*@
20035      * overide
20036      * 
20037      */
20038     isDirty : function() {
20039         if(this.disabled) {
20040             return false;
20041         }
20042         
20043         try {
20044             var d = Roo.decode(String(this.originalValue));
20045         } catch (e) {
20046             return String(this.getValue()) !== String(this.originalValue);
20047         }
20048         
20049         var originalValue = [];
20050         
20051         for (var i = 0; i < d.length; i++){
20052             originalValue.push(d[i][this.valueField]);
20053         }
20054         
20055         return String(this.getValue()) !== String(originalValue.join(','));
20056         
20057     }
20058     
20059 });
20060
20061
20062
20063 /**
20064  * @class Roo.form.ComboBoxArray.Item
20065  * @extends Roo.BoxComponent
20066  * A selected item in the list
20067  *  Fred [x]  Brian [x]  [Pick another |v]
20068  * 
20069  * @constructor
20070  * Create a new item.
20071  * @param {Object} config Configuration options
20072  */
20073  
20074 Roo.form.ComboBoxArray.Item = function(config) {
20075     config.id = Roo.id();
20076     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
20077 }
20078
20079 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
20080     data : {},
20081     cb: false,
20082     displayField : false,
20083     tipField : false,
20084     
20085     
20086     defaultAutoCreate : {
20087         tag: 'div',
20088         cls: 'x-cbarray-item',
20089         cn : [ 
20090             { tag: 'div' },
20091             {
20092                 tag: 'img',
20093                 width:16,
20094                 height : 16,
20095                 src : Roo.BLANK_IMAGE_URL ,
20096                 align: 'center'
20097             }
20098         ]
20099         
20100     },
20101     
20102  
20103     onRender : function(ct, position)
20104     {
20105         Roo.form.Field.superclass.onRender.call(this, ct, position);
20106         
20107         if(!this.el){
20108             var cfg = this.getAutoCreate();
20109             this.el = ct.createChild(cfg, position);
20110         }
20111         
20112         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
20113         
20114         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
20115             this.cb.renderer(this.data) :
20116             String.format('{0}',this.data[this.displayField]);
20117         
20118             
20119         this.el.child('div').dom.setAttribute('qtip',
20120                         String.format('{0}',this.data[this.tipField])
20121         );
20122         
20123         this.el.child('img').on('click', this.remove, this);
20124         
20125     },
20126    
20127     remove : function()
20128     {
20129         if(this.cb.disabled){
20130             return;
20131         }
20132         
20133         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
20134             this.cb.items.remove(this);
20135             this.el.child('img').un('click', this.remove, this);
20136             this.el.remove();
20137             this.cb.updateHiddenEl();
20138
20139             this.cb.fireEvent('remove', this.cb, this);
20140         }
20141         
20142     }
20143 });/*
20144  * Based on:
20145  * Ext JS Library 1.1.1
20146  * Copyright(c) 2006-2007, Ext JS, LLC.
20147  *
20148  * Originally Released Under LGPL - original licence link has changed is not relivant.
20149  *
20150  * Fork - LGPL
20151  * <script type="text/javascript">
20152  */
20153 /**
20154  * @class Roo.form.Checkbox
20155  * @extends Roo.form.Field
20156  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
20157  * @constructor
20158  * Creates a new Checkbox
20159  * @param {Object} config Configuration options
20160  */
20161 Roo.form.Checkbox = function(config){
20162     Roo.form.Checkbox.superclass.constructor.call(this, config);
20163     this.addEvents({
20164         /**
20165          * @event check
20166          * Fires when the checkbox is checked or unchecked.
20167              * @param {Roo.form.Checkbox} this This checkbox
20168              * @param {Boolean} checked The new checked value
20169              */
20170         check : true
20171     });
20172 };
20173
20174 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
20175     /**
20176      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
20177      */
20178     focusClass : undefined,
20179     /**
20180      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
20181      */
20182     fieldClass: "x-form-field",
20183     /**
20184      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
20185      */
20186     checked: false,
20187     /**
20188      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20189      * {tag: "input", type: "checkbox", autocomplete: "off"})
20190      */
20191     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
20192     /**
20193      * @cfg {String} boxLabel The text that appears beside the checkbox
20194      */
20195     boxLabel : "",
20196     /**
20197      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
20198      */  
20199     inputValue : '1',
20200     /**
20201      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20202      */
20203      valueOff: '0', // value when not checked..
20204
20205     actionMode : 'viewEl', 
20206     //
20207     // private
20208     itemCls : 'x-menu-check-item x-form-item',
20209     groupClass : 'x-menu-group-item',
20210     inputType : 'hidden',
20211     
20212     
20213     inSetChecked: false, // check that we are not calling self...
20214     
20215     inputElement: false, // real input element?
20216     basedOn: false, // ????
20217     
20218     isFormField: true, // not sure where this is needed!!!!
20219
20220     onResize : function(){
20221         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
20222         if(!this.boxLabel){
20223             this.el.alignTo(this.wrap, 'c-c');
20224         }
20225     },
20226
20227     initEvents : function(){
20228         Roo.form.Checkbox.superclass.initEvents.call(this);
20229         this.el.on("click", this.onClick,  this);
20230         this.el.on("change", this.onClick,  this);
20231     },
20232
20233
20234     getResizeEl : function(){
20235         return this.wrap;
20236     },
20237
20238     getPositionEl : function(){
20239         return this.wrap;
20240     },
20241
20242     // private
20243     onRender : function(ct, position){
20244         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20245         /*
20246         if(this.inputValue !== undefined){
20247             this.el.dom.value = this.inputValue;
20248         }
20249         */
20250         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20251         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20252         var viewEl = this.wrap.createChild({ 
20253             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20254         this.viewEl = viewEl;   
20255         this.wrap.on('click', this.onClick,  this); 
20256         
20257         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20258         this.el.on('propertychange', this.setFromHidden,  this);  //ie
20259         
20260         
20261         
20262         if(this.boxLabel){
20263             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20264         //    viewEl.on('click', this.onClick,  this); 
20265         }
20266         //if(this.checked){
20267             this.setChecked(this.checked);
20268         //}else{
20269             //this.checked = this.el.dom;
20270         //}
20271
20272     },
20273
20274     // private
20275     initValue : Roo.emptyFn,
20276
20277     /**
20278      * Returns the checked state of the checkbox.
20279      * @return {Boolean} True if checked, else false
20280      */
20281     getValue : function(){
20282         if(this.el){
20283             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
20284         }
20285         return this.valueOff;
20286         
20287     },
20288
20289         // private
20290     onClick : function(){ 
20291         if (this.disabled) {
20292             return;
20293         }
20294         this.setChecked(!this.checked);
20295
20296         //if(this.el.dom.checked != this.checked){
20297         //    this.setValue(this.el.dom.checked);
20298        // }
20299     },
20300
20301     /**
20302      * Sets the checked state of the checkbox.
20303      * On is always based on a string comparison between inputValue and the param.
20304      * @param {Boolean/String} value - the value to set 
20305      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
20306      */
20307     setValue : function(v,suppressEvent){
20308         
20309         
20310         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
20311         //if(this.el && this.el.dom){
20312         //    this.el.dom.checked = this.checked;
20313         //    this.el.dom.defaultChecked = this.checked;
20314         //}
20315         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
20316         //this.fireEvent("check", this, this.checked);
20317     },
20318     // private..
20319     setChecked : function(state,suppressEvent)
20320     {
20321         if (this.inSetChecked) {
20322             this.checked = state;
20323             return;
20324         }
20325         
20326     
20327         if(this.wrap){
20328             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
20329         }
20330         this.checked = state;
20331         if(suppressEvent !== true){
20332             this.fireEvent('check', this, state);
20333         }
20334         this.inSetChecked = true;
20335         this.el.dom.value = state ? this.inputValue : this.valueOff;
20336         this.inSetChecked = false;
20337         
20338     },
20339     // handle setting of hidden value by some other method!!?!?
20340     setFromHidden: function()
20341     {
20342         if(!this.el){
20343             return;
20344         }
20345         //console.log("SET FROM HIDDEN");
20346         //alert('setFrom hidden');
20347         this.setValue(this.el.dom.value);
20348     },
20349     
20350     onDestroy : function()
20351     {
20352         if(this.viewEl){
20353             Roo.get(this.viewEl).remove();
20354         }
20355          
20356         Roo.form.Checkbox.superclass.onDestroy.call(this);
20357     },
20358     
20359     setBoxLabel : function(str)
20360     {
20361         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
20362     }
20363
20364 });/*
20365  * Based on:
20366  * Ext JS Library 1.1.1
20367  * Copyright(c) 2006-2007, Ext JS, LLC.
20368  *
20369  * Originally Released Under LGPL - original licence link has changed is not relivant.
20370  *
20371  * Fork - LGPL
20372  * <script type="text/javascript">
20373  */
20374  
20375 /**
20376  * @class Roo.form.Radio
20377  * @extends Roo.form.Checkbox
20378  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
20379  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
20380  * @constructor
20381  * Creates a new Radio
20382  * @param {Object} config Configuration options
20383  */
20384 Roo.form.Radio = function(){
20385     Roo.form.Radio.superclass.constructor.apply(this, arguments);
20386 };
20387 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
20388     inputType: 'radio',
20389
20390     /**
20391      * If this radio is part of a group, it will return the selected value
20392      * @return {String}
20393      */
20394     getGroupValue : function(){
20395         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
20396     },
20397     
20398     
20399     onRender : function(ct, position){
20400         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20401         
20402         if(this.inputValue !== undefined){
20403             this.el.dom.value = this.inputValue;
20404         }
20405          
20406         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20407         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20408         //var viewEl = this.wrap.createChild({ 
20409         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20410         //this.viewEl = viewEl;   
20411         //this.wrap.on('click', this.onClick,  this); 
20412         
20413         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20414         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
20415         
20416         
20417         
20418         if(this.boxLabel){
20419             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20420         //    viewEl.on('click', this.onClick,  this); 
20421         }
20422          if(this.checked){
20423             this.el.dom.checked =   'checked' ;
20424         }
20425          
20426     } 
20427     
20428     
20429 });//<script type="text/javascript">
20430
20431 /*
20432  * Based  Ext JS Library 1.1.1
20433  * Copyright(c) 2006-2007, Ext JS, LLC.
20434  * LGPL
20435  *
20436  */
20437  
20438 /**
20439  * @class Roo.HtmlEditorCore
20440  * @extends Roo.Component
20441  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20442  *
20443  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20444  */
20445
20446 Roo.HtmlEditorCore = function(config){
20447     
20448     
20449     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20450     
20451     
20452     this.addEvents({
20453         /**
20454          * @event initialize
20455          * Fires when the editor is fully initialized (including the iframe)
20456          * @param {Roo.HtmlEditorCore} this
20457          */
20458         initialize: true,
20459         /**
20460          * @event activate
20461          * Fires when the editor is first receives the focus. Any insertion must wait
20462          * until after this event.
20463          * @param {Roo.HtmlEditorCore} this
20464          */
20465         activate: true,
20466          /**
20467          * @event beforesync
20468          * Fires before the textarea is updated with content from the editor iframe. Return false
20469          * to cancel the sync.
20470          * @param {Roo.HtmlEditorCore} this
20471          * @param {String} html
20472          */
20473         beforesync: true,
20474          /**
20475          * @event beforepush
20476          * Fires before the iframe editor is updated with content from the textarea. Return false
20477          * to cancel the push.
20478          * @param {Roo.HtmlEditorCore} this
20479          * @param {String} html
20480          */
20481         beforepush: true,
20482          /**
20483          * @event sync
20484          * Fires when the textarea is updated with content from the editor iframe.
20485          * @param {Roo.HtmlEditorCore} this
20486          * @param {String} html
20487          */
20488         sync: true,
20489          /**
20490          * @event push
20491          * Fires when the iframe editor is updated with content from the textarea.
20492          * @param {Roo.HtmlEditorCore} this
20493          * @param {String} html
20494          */
20495         push: true,
20496         
20497         /**
20498          * @event editorevent
20499          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20500          * @param {Roo.HtmlEditorCore} this
20501          */
20502         editorevent: true
20503         
20504     });
20505     
20506     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20507     
20508     // defaults : white / black...
20509     this.applyBlacklists();
20510     
20511     
20512     
20513 };
20514
20515
20516 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
20517
20518
20519      /**
20520      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
20521      */
20522     
20523     owner : false,
20524     
20525      /**
20526      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20527      *                        Roo.resizable.
20528      */
20529     resizable : false,
20530      /**
20531      * @cfg {Number} height (in pixels)
20532      */   
20533     height: 300,
20534    /**
20535      * @cfg {Number} width (in pixels)
20536      */   
20537     width: 500,
20538     
20539     /**
20540      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20541      * 
20542      */
20543     stylesheets: false,
20544     
20545     // id of frame..
20546     frameId: false,
20547     
20548     // private properties
20549     validationEvent : false,
20550     deferHeight: true,
20551     initialized : false,
20552     activated : false,
20553     sourceEditMode : false,
20554     onFocus : Roo.emptyFn,
20555     iframePad:3,
20556     hideMode:'offsets',
20557     
20558     clearUp: true,
20559     
20560     // blacklist + whitelisted elements..
20561     black: false,
20562     white: false,
20563      
20564     bodyCls : '',
20565
20566     /**
20567      * Protected method that will not generally be called directly. It
20568      * is called when the editor initializes the iframe with HTML contents. Override this method if you
20569      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20570      */
20571     getDocMarkup : function(){
20572         // body styles..
20573         var st = '';
20574         
20575         // inherit styels from page...?? 
20576         if (this.stylesheets === false) {
20577             
20578             Roo.get(document.head).select('style').each(function(node) {
20579                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20580             });
20581             
20582             Roo.get(document.head).select('link').each(function(node) { 
20583                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20584             });
20585             
20586         } else if (!this.stylesheets.length) {
20587                 // simple..
20588                 st = '<style type="text/css">' +
20589                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20590                    '</style>';
20591         } else { 
20592             st = '<style type="text/css">' +
20593                     this.stylesheets +
20594                 '</style>';
20595         }
20596         
20597         st +=  '<style type="text/css">' +
20598             'IMG { cursor: pointer } ' +
20599         '</style>';
20600
20601         var cls = 'roo-htmleditor-body';
20602         
20603         if(this.bodyCls.length){
20604             cls += ' ' + this.bodyCls;
20605         }
20606         
20607         return '<html><head>' + st  +
20608             //<style type="text/css">' +
20609             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20610             //'</style>' +
20611             ' </head><body class="' +  cls + '"></body></html>';
20612     },
20613
20614     // private
20615     onRender : function(ct, position)
20616     {
20617         var _t = this;
20618         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20619         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20620         
20621         
20622         this.el.dom.style.border = '0 none';
20623         this.el.dom.setAttribute('tabIndex', -1);
20624         this.el.addClass('x-hidden hide');
20625         
20626         
20627         
20628         if(Roo.isIE){ // fix IE 1px bogus margin
20629             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20630         }
20631        
20632         
20633         this.frameId = Roo.id();
20634         
20635          
20636         
20637         var iframe = this.owner.wrap.createChild({
20638             tag: 'iframe',
20639             cls: 'form-control', // bootstrap..
20640             id: this.frameId,
20641             name: this.frameId,
20642             frameBorder : 'no',
20643             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
20644         }, this.el
20645         );
20646         
20647         
20648         this.iframe = iframe.dom;
20649
20650          this.assignDocWin();
20651         
20652         this.doc.designMode = 'on';
20653        
20654         this.doc.open();
20655         this.doc.write(this.getDocMarkup());
20656         this.doc.close();
20657
20658         
20659         var task = { // must defer to wait for browser to be ready
20660             run : function(){
20661                 //console.log("run task?" + this.doc.readyState);
20662                 this.assignDocWin();
20663                 if(this.doc.body || this.doc.readyState == 'complete'){
20664                     try {
20665                         this.doc.designMode="on";
20666                     } catch (e) {
20667                         return;
20668                     }
20669                     Roo.TaskMgr.stop(task);
20670                     this.initEditor.defer(10, this);
20671                 }
20672             },
20673             interval : 10,
20674             duration: 10000,
20675             scope: this
20676         };
20677         Roo.TaskMgr.start(task);
20678
20679     },
20680
20681     // private
20682     onResize : function(w, h)
20683     {
20684          Roo.log('resize: ' +w + ',' + h );
20685         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20686         if(!this.iframe){
20687             return;
20688         }
20689         if(typeof w == 'number'){
20690             
20691             this.iframe.style.width = w + 'px';
20692         }
20693         if(typeof h == 'number'){
20694             
20695             this.iframe.style.height = h + 'px';
20696             if(this.doc){
20697                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20698             }
20699         }
20700         
20701     },
20702
20703     /**
20704      * Toggles the editor between standard and source edit mode.
20705      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20706      */
20707     toggleSourceEdit : function(sourceEditMode){
20708         
20709         this.sourceEditMode = sourceEditMode === true;
20710         
20711         if(this.sourceEditMode){
20712  
20713             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
20714             
20715         }else{
20716             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20717             //this.iframe.className = '';
20718             this.deferFocus();
20719         }
20720         //this.setSize(this.owner.wrap.getSize());
20721         //this.fireEvent('editmodechange', this, this.sourceEditMode);
20722     },
20723
20724     
20725   
20726
20727     /**
20728      * Protected method that will not generally be called directly. If you need/want
20729      * custom HTML cleanup, this is the method you should override.
20730      * @param {String} html The HTML to be cleaned
20731      * return {String} The cleaned HTML
20732      */
20733     cleanHtml : function(html){
20734         html = String(html);
20735         if(html.length > 5){
20736             if(Roo.isSafari){ // strip safari nonsense
20737                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20738             }
20739         }
20740         if(html == '&nbsp;'){
20741             html = '';
20742         }
20743         return html;
20744     },
20745
20746     /**
20747      * HTML Editor -> Textarea
20748      * Protected method that will not generally be called directly. Syncs the contents
20749      * of the editor iframe with the textarea.
20750      */
20751     syncValue : function(){
20752         if(this.initialized){
20753             var bd = (this.doc.body || this.doc.documentElement);
20754             //this.cleanUpPaste(); -- this is done else where and causes havoc..
20755             var html = bd.innerHTML;
20756             if(Roo.isSafari){
20757                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20758                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20759                 if(m && m[1]){
20760                     html = '<div style="'+m[0]+'">' + html + '</div>';
20761                 }
20762             }
20763             html = this.cleanHtml(html);
20764             // fix up the special chars.. normaly like back quotes in word...
20765             // however we do not want to do this with chinese..
20766             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
20767                 var cc = b.charCodeAt();
20768                 if (
20769                     (cc >= 0x4E00 && cc < 0xA000 ) ||
20770                     (cc >= 0x3400 && cc < 0x4E00 ) ||
20771                     (cc >= 0xf900 && cc < 0xfb00 )
20772                 ) {
20773                         return b;
20774                 }
20775                 return "&#"+cc+";" 
20776             });
20777             if(this.owner.fireEvent('beforesync', this, html) !== false){
20778                 this.el.dom.value = html;
20779                 this.owner.fireEvent('sync', this, html);
20780             }
20781         }
20782     },
20783
20784     /**
20785      * Protected method that will not generally be called directly. Pushes the value of the textarea
20786      * into the iframe editor.
20787      */
20788     pushValue : function(){
20789         if(this.initialized){
20790             var v = this.el.dom.value.trim();
20791             
20792 //            if(v.length < 1){
20793 //                v = '&#160;';
20794 //            }
20795             
20796             if(this.owner.fireEvent('beforepush', this, v) !== false){
20797                 var d = (this.doc.body || this.doc.documentElement);
20798                 d.innerHTML = v;
20799                 this.cleanUpPaste();
20800                 this.el.dom.value = d.innerHTML;
20801                 this.owner.fireEvent('push', this, v);
20802             }
20803         }
20804     },
20805
20806     // private
20807     deferFocus : function(){
20808         this.focus.defer(10, this);
20809     },
20810
20811     // doc'ed in Field
20812     focus : function(){
20813         if(this.win && !this.sourceEditMode){
20814             this.win.focus();
20815         }else{
20816             this.el.focus();
20817         }
20818     },
20819     
20820     assignDocWin: function()
20821     {
20822         var iframe = this.iframe;
20823         
20824          if(Roo.isIE){
20825             this.doc = iframe.contentWindow.document;
20826             this.win = iframe.contentWindow;
20827         } else {
20828 //            if (!Roo.get(this.frameId)) {
20829 //                return;
20830 //            }
20831 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20832 //            this.win = Roo.get(this.frameId).dom.contentWindow;
20833             
20834             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20835                 return;
20836             }
20837             
20838             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20839             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20840         }
20841     },
20842     
20843     // private
20844     initEditor : function(){
20845         //console.log("INIT EDITOR");
20846         this.assignDocWin();
20847         
20848         
20849         
20850         this.doc.designMode="on";
20851         this.doc.open();
20852         this.doc.write(this.getDocMarkup());
20853         this.doc.close();
20854         
20855         var dbody = (this.doc.body || this.doc.documentElement);
20856         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20857         // this copies styles from the containing element into thsi one..
20858         // not sure why we need all of this..
20859         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20860         
20861         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20862         //ss['background-attachment'] = 'fixed'; // w3c
20863         dbody.bgProperties = 'fixed'; // ie
20864         //Roo.DomHelper.applyStyles(dbody, ss);
20865         Roo.EventManager.on(this.doc, {
20866             //'mousedown': this.onEditorEvent,
20867             'mouseup': this.onEditorEvent,
20868             'dblclick': this.onEditorEvent,
20869             'click': this.onEditorEvent,
20870             'keyup': this.onEditorEvent,
20871             buffer:100,
20872             scope: this
20873         });
20874         if(Roo.isGecko){
20875             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20876         }
20877         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20878             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20879         }
20880         this.initialized = true;
20881
20882         this.owner.fireEvent('initialize', this);
20883         this.pushValue();
20884     },
20885
20886     // private
20887     onDestroy : function(){
20888         
20889         
20890         
20891         if(this.rendered){
20892             
20893             //for (var i =0; i < this.toolbars.length;i++) {
20894             //    // fixme - ask toolbars for heights?
20895             //    this.toolbars[i].onDestroy();
20896            // }
20897             
20898             //this.wrap.dom.innerHTML = '';
20899             //this.wrap.remove();
20900         }
20901     },
20902
20903     // private
20904     onFirstFocus : function(){
20905         
20906         this.assignDocWin();
20907         
20908         
20909         this.activated = true;
20910          
20911     
20912         if(Roo.isGecko){ // prevent silly gecko errors
20913             this.win.focus();
20914             var s = this.win.getSelection();
20915             if(!s.focusNode || s.focusNode.nodeType != 3){
20916                 var r = s.getRangeAt(0);
20917                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
20918                 r.collapse(true);
20919                 this.deferFocus();
20920             }
20921             try{
20922                 this.execCmd('useCSS', true);
20923                 this.execCmd('styleWithCSS', false);
20924             }catch(e){}
20925         }
20926         this.owner.fireEvent('activate', this);
20927     },
20928
20929     // private
20930     adjustFont: function(btn){
20931         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
20932         //if(Roo.isSafari){ // safari
20933         //    adjust *= 2;
20934        // }
20935         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
20936         if(Roo.isSafari){ // safari
20937             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
20938             v =  (v < 10) ? 10 : v;
20939             v =  (v > 48) ? 48 : v;
20940             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
20941             
20942         }
20943         
20944         
20945         v = Math.max(1, v+adjust);
20946         
20947         this.execCmd('FontSize', v  );
20948     },
20949
20950     onEditorEvent : function(e)
20951     {
20952         this.owner.fireEvent('editorevent', this, e);
20953       //  this.updateToolbar();
20954         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
20955     },
20956
20957     insertTag : function(tg)
20958     {
20959         // could be a bit smarter... -> wrap the current selected tRoo..
20960         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
20961             
20962             range = this.createRange(this.getSelection());
20963             var wrappingNode = this.doc.createElement(tg.toLowerCase());
20964             wrappingNode.appendChild(range.extractContents());
20965             range.insertNode(wrappingNode);
20966
20967             return;
20968             
20969             
20970             
20971         }
20972         this.execCmd("formatblock",   tg);
20973         
20974     },
20975     
20976     insertText : function(txt)
20977     {
20978         
20979         
20980         var range = this.createRange();
20981         range.deleteContents();
20982                //alert(Sender.getAttribute('label'));
20983                
20984         range.insertNode(this.doc.createTextNode(txt));
20985     } ,
20986     
20987      
20988
20989     /**
20990      * Executes a Midas editor command on the editor document and performs necessary focus and
20991      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
20992      * @param {String} cmd The Midas command
20993      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20994      */
20995     relayCmd : function(cmd, value){
20996         this.win.focus();
20997         this.execCmd(cmd, value);
20998         this.owner.fireEvent('editorevent', this);
20999         //this.updateToolbar();
21000         this.owner.deferFocus();
21001     },
21002
21003     /**
21004      * Executes a Midas editor command directly on the editor document.
21005      * For visual commands, you should use {@link #relayCmd} instead.
21006      * <b>This should only be called after the editor is initialized.</b>
21007      * @param {String} cmd The Midas command
21008      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21009      */
21010     execCmd : function(cmd, value){
21011         this.doc.execCommand(cmd, false, value === undefined ? null : value);
21012         this.syncValue();
21013     },
21014  
21015  
21016    
21017     /**
21018      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21019      * to insert tRoo.
21020      * @param {String} text | dom node.. 
21021      */
21022     insertAtCursor : function(text)
21023     {
21024         
21025         if(!this.activated){
21026             return;
21027         }
21028         /*
21029         if(Roo.isIE){
21030             this.win.focus();
21031             var r = this.doc.selection.createRange();
21032             if(r){
21033                 r.collapse(true);
21034                 r.pasteHTML(text);
21035                 this.syncValue();
21036                 this.deferFocus();
21037             
21038             }
21039             return;
21040         }
21041         */
21042         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21043             this.win.focus();
21044             
21045             
21046             // from jquery ui (MIT licenced)
21047             var range, node;
21048             var win = this.win;
21049             
21050             if (win.getSelection && win.getSelection().getRangeAt) {
21051                 range = win.getSelection().getRangeAt(0);
21052                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21053                 range.insertNode(node);
21054             } else if (win.document.selection && win.document.selection.createRange) {
21055                 // no firefox support
21056                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21057                 win.document.selection.createRange().pasteHTML(txt);
21058             } else {
21059                 // no firefox support
21060                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21061                 this.execCmd('InsertHTML', txt);
21062             } 
21063             
21064             this.syncValue();
21065             
21066             this.deferFocus();
21067         }
21068     },
21069  // private
21070     mozKeyPress : function(e){
21071         if(e.ctrlKey){
21072             var c = e.getCharCode(), cmd;
21073           
21074             if(c > 0){
21075                 c = String.fromCharCode(c).toLowerCase();
21076                 switch(c){
21077                     case 'b':
21078                         cmd = 'bold';
21079                         break;
21080                     case 'i':
21081                         cmd = 'italic';
21082                         break;
21083                     
21084                     case 'u':
21085                         cmd = 'underline';
21086                         break;
21087                     
21088                     case 'v':
21089                         this.cleanUpPaste.defer(100, this);
21090                         return;
21091                         
21092                 }
21093                 if(cmd){
21094                     this.win.focus();
21095                     this.execCmd(cmd);
21096                     this.deferFocus();
21097                     e.preventDefault();
21098                 }
21099                 
21100             }
21101         }
21102     },
21103
21104     // private
21105     fixKeys : function(){ // load time branching for fastest keydown performance
21106         if(Roo.isIE){
21107             return function(e){
21108                 var k = e.getKey(), r;
21109                 if(k == e.TAB){
21110                     e.stopEvent();
21111                     r = this.doc.selection.createRange();
21112                     if(r){
21113                         r.collapse(true);
21114                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21115                         this.deferFocus();
21116                     }
21117                     return;
21118                 }
21119                 
21120                 if(k == e.ENTER){
21121                     r = this.doc.selection.createRange();
21122                     if(r){
21123                         var target = r.parentElement();
21124                         if(!target || target.tagName.toLowerCase() != 'li'){
21125                             e.stopEvent();
21126                             r.pasteHTML('<br />');
21127                             r.collapse(false);
21128                             r.select();
21129                         }
21130                     }
21131                 }
21132                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21133                     this.cleanUpPaste.defer(100, this);
21134                     return;
21135                 }
21136                 
21137                 
21138             };
21139         }else if(Roo.isOpera){
21140             return function(e){
21141                 var k = e.getKey();
21142                 if(k == e.TAB){
21143                     e.stopEvent();
21144                     this.win.focus();
21145                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21146                     this.deferFocus();
21147                 }
21148                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21149                     this.cleanUpPaste.defer(100, this);
21150                     return;
21151                 }
21152                 
21153             };
21154         }else if(Roo.isSafari){
21155             return function(e){
21156                 var k = e.getKey();
21157                 
21158                 if(k == e.TAB){
21159                     e.stopEvent();
21160                     this.execCmd('InsertText','\t');
21161                     this.deferFocus();
21162                     return;
21163                 }
21164                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21165                     this.cleanUpPaste.defer(100, this);
21166                     return;
21167                 }
21168                 
21169              };
21170         }
21171     }(),
21172     
21173     getAllAncestors: function()
21174     {
21175         var p = this.getSelectedNode();
21176         var a = [];
21177         if (!p) {
21178             a.push(p); // push blank onto stack..
21179             p = this.getParentElement();
21180         }
21181         
21182         
21183         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21184             a.push(p);
21185             p = p.parentNode;
21186         }
21187         a.push(this.doc.body);
21188         return a;
21189     },
21190     lastSel : false,
21191     lastSelNode : false,
21192     
21193     
21194     getSelection : function() 
21195     {
21196         this.assignDocWin();
21197         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21198     },
21199     
21200     getSelectedNode: function() 
21201     {
21202         // this may only work on Gecko!!!
21203         
21204         // should we cache this!!!!
21205         
21206         
21207         
21208          
21209         var range = this.createRange(this.getSelection()).cloneRange();
21210         
21211         if (Roo.isIE) {
21212             var parent = range.parentElement();
21213             while (true) {
21214                 var testRange = range.duplicate();
21215                 testRange.moveToElementText(parent);
21216                 if (testRange.inRange(range)) {
21217                     break;
21218                 }
21219                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21220                     break;
21221                 }
21222                 parent = parent.parentElement;
21223             }
21224             return parent;
21225         }
21226         
21227         // is ancestor a text element.
21228         var ac =  range.commonAncestorContainer;
21229         if (ac.nodeType == 3) {
21230             ac = ac.parentNode;
21231         }
21232         
21233         var ar = ac.childNodes;
21234          
21235         var nodes = [];
21236         var other_nodes = [];
21237         var has_other_nodes = false;
21238         for (var i=0;i<ar.length;i++) {
21239             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
21240                 continue;
21241             }
21242             // fullly contained node.
21243             
21244             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21245                 nodes.push(ar[i]);
21246                 continue;
21247             }
21248             
21249             // probably selected..
21250             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21251                 other_nodes.push(ar[i]);
21252                 continue;
21253             }
21254             // outer..
21255             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
21256                 continue;
21257             }
21258             
21259             
21260             has_other_nodes = true;
21261         }
21262         if (!nodes.length && other_nodes.length) {
21263             nodes= other_nodes;
21264         }
21265         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21266             return false;
21267         }
21268         
21269         return nodes[0];
21270     },
21271     createRange: function(sel)
21272     {
21273         // this has strange effects when using with 
21274         // top toolbar - not sure if it's a great idea.
21275         //this.editor.contentWindow.focus();
21276         if (typeof sel != "undefined") {
21277             try {
21278                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21279             } catch(e) {
21280                 return this.doc.createRange();
21281             }
21282         } else {
21283             return this.doc.createRange();
21284         }
21285     },
21286     getParentElement: function()
21287     {
21288         
21289         this.assignDocWin();
21290         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21291         
21292         var range = this.createRange(sel);
21293          
21294         try {
21295             var p = range.commonAncestorContainer;
21296             while (p.nodeType == 3) { // text node
21297                 p = p.parentNode;
21298             }
21299             return p;
21300         } catch (e) {
21301             return null;
21302         }
21303     
21304     },
21305     /***
21306      *
21307      * Range intersection.. the hard stuff...
21308      *  '-1' = before
21309      *  '0' = hits..
21310      *  '1' = after.
21311      *         [ -- selected range --- ]
21312      *   [fail]                        [fail]
21313      *
21314      *    basically..
21315      *      if end is before start or  hits it. fail.
21316      *      if start is after end or hits it fail.
21317      *
21318      *   if either hits (but other is outside. - then it's not 
21319      *   
21320      *    
21321      **/
21322     
21323     
21324     // @see http://www.thismuchiknow.co.uk/?p=64.
21325     rangeIntersectsNode : function(range, node)
21326     {
21327         var nodeRange = node.ownerDocument.createRange();
21328         try {
21329             nodeRange.selectNode(node);
21330         } catch (e) {
21331             nodeRange.selectNodeContents(node);
21332         }
21333     
21334         var rangeStartRange = range.cloneRange();
21335         rangeStartRange.collapse(true);
21336     
21337         var rangeEndRange = range.cloneRange();
21338         rangeEndRange.collapse(false);
21339     
21340         var nodeStartRange = nodeRange.cloneRange();
21341         nodeStartRange.collapse(true);
21342     
21343         var nodeEndRange = nodeRange.cloneRange();
21344         nodeEndRange.collapse(false);
21345     
21346         return rangeStartRange.compareBoundaryPoints(
21347                  Range.START_TO_START, nodeEndRange) == -1 &&
21348                rangeEndRange.compareBoundaryPoints(
21349                  Range.START_TO_START, nodeStartRange) == 1;
21350         
21351          
21352     },
21353     rangeCompareNode : function(range, node)
21354     {
21355         var nodeRange = node.ownerDocument.createRange();
21356         try {
21357             nodeRange.selectNode(node);
21358         } catch (e) {
21359             nodeRange.selectNodeContents(node);
21360         }
21361         
21362         
21363         range.collapse(true);
21364     
21365         nodeRange.collapse(true);
21366      
21367         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21368         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
21369          
21370         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21371         
21372         var nodeIsBefore   =  ss == 1;
21373         var nodeIsAfter    = ee == -1;
21374         
21375         if (nodeIsBefore && nodeIsAfter) {
21376             return 0; // outer
21377         }
21378         if (!nodeIsBefore && nodeIsAfter) {
21379             return 1; //right trailed.
21380         }
21381         
21382         if (nodeIsBefore && !nodeIsAfter) {
21383             return 2;  // left trailed.
21384         }
21385         // fully contined.
21386         return 3;
21387     },
21388
21389     // private? - in a new class?
21390     cleanUpPaste :  function()
21391     {
21392         // cleans up the whole document..
21393         Roo.log('cleanuppaste');
21394         
21395         this.cleanUpChildren(this.doc.body);
21396         var clean = this.cleanWordChars(this.doc.body.innerHTML);
21397         if (clean != this.doc.body.innerHTML) {
21398             this.doc.body.innerHTML = clean;
21399         }
21400         
21401     },
21402     
21403     cleanWordChars : function(input) {// change the chars to hex code
21404         var he = Roo.HtmlEditorCore;
21405         
21406         var output = input;
21407         Roo.each(he.swapCodes, function(sw) { 
21408             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21409             
21410             output = output.replace(swapper, sw[1]);
21411         });
21412         
21413         return output;
21414     },
21415     
21416     
21417     cleanUpChildren : function (n)
21418     {
21419         if (!n.childNodes.length) {
21420             return;
21421         }
21422         for (var i = n.childNodes.length-1; i > -1 ; i--) {
21423            this.cleanUpChild(n.childNodes[i]);
21424         }
21425     },
21426     
21427     
21428         
21429     
21430     cleanUpChild : function (node)
21431     {
21432         var ed = this;
21433         //console.log(node);
21434         if (node.nodeName == "#text") {
21435             // clean up silly Windows -- stuff?
21436             return; 
21437         }
21438         if (node.nodeName == "#comment") {
21439             node.parentNode.removeChild(node);
21440             // clean up silly Windows -- stuff?
21441             return; 
21442         }
21443         var lcname = node.tagName.toLowerCase();
21444         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21445         // whitelist of tags..
21446         
21447         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21448             // remove node.
21449             node.parentNode.removeChild(node);
21450             return;
21451             
21452         }
21453         
21454         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21455         
21456         // remove <a name=....> as rendering on yahoo mailer is borked with this.
21457         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21458         
21459         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21460         //    remove_keep_children = true;
21461         //}
21462         
21463         if (remove_keep_children) {
21464             this.cleanUpChildren(node);
21465             // inserts everything just before this node...
21466             while (node.childNodes.length) {
21467                 var cn = node.childNodes[0];
21468                 node.removeChild(cn);
21469                 node.parentNode.insertBefore(cn, node);
21470             }
21471             node.parentNode.removeChild(node);
21472             return;
21473         }
21474         
21475         if (!node.attributes || !node.attributes.length) {
21476             this.cleanUpChildren(node);
21477             return;
21478         }
21479         
21480         function cleanAttr(n,v)
21481         {
21482             
21483             if (v.match(/^\./) || v.match(/^\//)) {
21484                 return;
21485             }
21486             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
21487                 return;
21488             }
21489             if (v.match(/^#/)) {
21490                 return;
21491             }
21492 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21493             node.removeAttribute(n);
21494             
21495         }
21496         
21497         var cwhite = this.cwhite;
21498         var cblack = this.cblack;
21499             
21500         function cleanStyle(n,v)
21501         {
21502             if (v.match(/expression/)) { //XSS?? should we even bother..
21503                 node.removeAttribute(n);
21504                 return;
21505             }
21506             
21507             var parts = v.split(/;/);
21508             var clean = [];
21509             
21510             Roo.each(parts, function(p) {
21511                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21512                 if (!p.length) {
21513                     return true;
21514                 }
21515                 var l = p.split(':').shift().replace(/\s+/g,'');
21516                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21517                 
21518                 if ( cwhite.length && cblack.indexOf(l) > -1) {
21519 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21520                     //node.removeAttribute(n);
21521                     return true;
21522                 }
21523                 //Roo.log()
21524                 // only allow 'c whitelisted system attributes'
21525                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
21526 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21527                     //node.removeAttribute(n);
21528                     return true;
21529                 }
21530                 
21531                 
21532                  
21533                 
21534                 clean.push(p);
21535                 return true;
21536             });
21537             if (clean.length) { 
21538                 node.setAttribute(n, clean.join(';'));
21539             } else {
21540                 node.removeAttribute(n);
21541             }
21542             
21543         }
21544         
21545         
21546         for (var i = node.attributes.length-1; i > -1 ; i--) {
21547             var a = node.attributes[i];
21548             //console.log(a);
21549             
21550             if (a.name.toLowerCase().substr(0,2)=='on')  {
21551                 node.removeAttribute(a.name);
21552                 continue;
21553             }
21554             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21555                 node.removeAttribute(a.name);
21556                 continue;
21557             }
21558             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21559                 cleanAttr(a.name,a.value); // fixme..
21560                 continue;
21561             }
21562             if (a.name == 'style') {
21563                 cleanStyle(a.name,a.value);
21564                 continue;
21565             }
21566             /// clean up MS crap..
21567             // tecnically this should be a list of valid class'es..
21568             
21569             
21570             if (a.name == 'class') {
21571                 if (a.value.match(/^Mso/)) {
21572                     node.className = '';
21573                 }
21574                 
21575                 if (a.value.match(/^body$/)) {
21576                     node.className = '';
21577                 }
21578                 continue;
21579             }
21580             
21581             // style cleanup!?
21582             // class cleanup?
21583             
21584         }
21585         
21586         
21587         this.cleanUpChildren(node);
21588         
21589         
21590     },
21591     
21592     /**
21593      * Clean up MS wordisms...
21594      */
21595     cleanWord : function(node)
21596     {
21597         
21598         
21599         if (!node) {
21600             this.cleanWord(this.doc.body);
21601             return;
21602         }
21603         if (node.nodeName == "#text") {
21604             // clean up silly Windows -- stuff?
21605             return; 
21606         }
21607         if (node.nodeName == "#comment") {
21608             node.parentNode.removeChild(node);
21609             // clean up silly Windows -- stuff?
21610             return; 
21611         }
21612         
21613         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21614             node.parentNode.removeChild(node);
21615             return;
21616         }
21617         
21618         // remove - but keep children..
21619         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
21620             while (node.childNodes.length) {
21621                 var cn = node.childNodes[0];
21622                 node.removeChild(cn);
21623                 node.parentNode.insertBefore(cn, node);
21624             }
21625             node.parentNode.removeChild(node);
21626             this.iterateChildren(node, this.cleanWord);
21627             return;
21628         }
21629         // clean styles
21630         if (node.className.length) {
21631             
21632             var cn = node.className.split(/\W+/);
21633             var cna = [];
21634             Roo.each(cn, function(cls) {
21635                 if (cls.match(/Mso[a-zA-Z]+/)) {
21636                     return;
21637                 }
21638                 cna.push(cls);
21639             });
21640             node.className = cna.length ? cna.join(' ') : '';
21641             if (!cna.length) {
21642                 node.removeAttribute("class");
21643             }
21644         }
21645         
21646         if (node.hasAttribute("lang")) {
21647             node.removeAttribute("lang");
21648         }
21649         
21650         if (node.hasAttribute("style")) {
21651             
21652             var styles = node.getAttribute("style").split(";");
21653             var nstyle = [];
21654             Roo.each(styles, function(s) {
21655                 if (!s.match(/:/)) {
21656                     return;
21657                 }
21658                 var kv = s.split(":");
21659                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21660                     return;
21661                 }
21662                 // what ever is left... we allow.
21663                 nstyle.push(s);
21664             });
21665             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21666             if (!nstyle.length) {
21667                 node.removeAttribute('style');
21668             }
21669         }
21670         this.iterateChildren(node, this.cleanWord);
21671         
21672         
21673         
21674     },
21675     /**
21676      * iterateChildren of a Node, calling fn each time, using this as the scole..
21677      * @param {DomNode} node node to iterate children of.
21678      * @param {Function} fn method of this class to call on each item.
21679      */
21680     iterateChildren : function(node, fn)
21681     {
21682         if (!node.childNodes.length) {
21683                 return;
21684         }
21685         for (var i = node.childNodes.length-1; i > -1 ; i--) {
21686            fn.call(this, node.childNodes[i])
21687         }
21688     },
21689     
21690     
21691     /**
21692      * cleanTableWidths.
21693      *
21694      * Quite often pasting from word etc.. results in tables with column and widths.
21695      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21696      *
21697      */
21698     cleanTableWidths : function(node)
21699     {
21700          
21701          
21702         if (!node) {
21703             this.cleanTableWidths(this.doc.body);
21704             return;
21705         }
21706         
21707         // ignore list...
21708         if (node.nodeName == "#text" || node.nodeName == "#comment") {
21709             return; 
21710         }
21711         Roo.log(node.tagName);
21712         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21713             this.iterateChildren(node, this.cleanTableWidths);
21714             return;
21715         }
21716         if (node.hasAttribute('width')) {
21717             node.removeAttribute('width');
21718         }
21719         
21720          
21721         if (node.hasAttribute("style")) {
21722             // pretty basic...
21723             
21724             var styles = node.getAttribute("style").split(";");
21725             var nstyle = [];
21726             Roo.each(styles, function(s) {
21727                 if (!s.match(/:/)) {
21728                     return;
21729                 }
21730                 var kv = s.split(":");
21731                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21732                     return;
21733                 }
21734                 // what ever is left... we allow.
21735                 nstyle.push(s);
21736             });
21737             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21738             if (!nstyle.length) {
21739                 node.removeAttribute('style');
21740             }
21741         }
21742         
21743         this.iterateChildren(node, this.cleanTableWidths);
21744         
21745         
21746     },
21747     
21748     
21749     
21750     
21751     domToHTML : function(currentElement, depth, nopadtext) {
21752         
21753         depth = depth || 0;
21754         nopadtext = nopadtext || false;
21755     
21756         if (!currentElement) {
21757             return this.domToHTML(this.doc.body);
21758         }
21759         
21760         //Roo.log(currentElement);
21761         var j;
21762         var allText = false;
21763         var nodeName = currentElement.nodeName;
21764         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21765         
21766         if  (nodeName == '#text') {
21767             
21768             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21769         }
21770         
21771         
21772         var ret = '';
21773         if (nodeName != 'BODY') {
21774              
21775             var i = 0;
21776             // Prints the node tagName, such as <A>, <IMG>, etc
21777             if (tagName) {
21778                 var attr = [];
21779                 for(i = 0; i < currentElement.attributes.length;i++) {
21780                     // quoting?
21781                     var aname = currentElement.attributes.item(i).name;
21782                     if (!currentElement.attributes.item(i).value.length) {
21783                         continue;
21784                     }
21785                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21786                 }
21787                 
21788                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21789             } 
21790             else {
21791                 
21792                 // eack
21793             }
21794         } else {
21795             tagName = false;
21796         }
21797         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21798             return ret;
21799         }
21800         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21801             nopadtext = true;
21802         }
21803         
21804         
21805         // Traverse the tree
21806         i = 0;
21807         var currentElementChild = currentElement.childNodes.item(i);
21808         var allText = true;
21809         var innerHTML  = '';
21810         lastnode = '';
21811         while (currentElementChild) {
21812             // Formatting code (indent the tree so it looks nice on the screen)
21813             var nopad = nopadtext;
21814             if (lastnode == 'SPAN') {
21815                 nopad  = true;
21816             }
21817             // text
21818             if  (currentElementChild.nodeName == '#text') {
21819                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21820                 toadd = nopadtext ? toadd : toadd.trim();
21821                 if (!nopad && toadd.length > 80) {
21822                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
21823                 }
21824                 innerHTML  += toadd;
21825                 
21826                 i++;
21827                 currentElementChild = currentElement.childNodes.item(i);
21828                 lastNode = '';
21829                 continue;
21830             }
21831             allText = false;
21832             
21833             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
21834                 
21835             // Recursively traverse the tree structure of the child node
21836             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
21837             lastnode = currentElementChild.nodeName;
21838             i++;
21839             currentElementChild=currentElement.childNodes.item(i);
21840         }
21841         
21842         ret += innerHTML;
21843         
21844         if (!allText) {
21845                 // The remaining code is mostly for formatting the tree
21846             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
21847         }
21848         
21849         
21850         if (tagName) {
21851             ret+= "</"+tagName+">";
21852         }
21853         return ret;
21854         
21855     },
21856         
21857     applyBlacklists : function()
21858     {
21859         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
21860         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
21861         
21862         this.white = [];
21863         this.black = [];
21864         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
21865             if (b.indexOf(tag) > -1) {
21866                 return;
21867             }
21868             this.white.push(tag);
21869             
21870         }, this);
21871         
21872         Roo.each(w, function(tag) {
21873             if (b.indexOf(tag) > -1) {
21874                 return;
21875             }
21876             if (this.white.indexOf(tag) > -1) {
21877                 return;
21878             }
21879             this.white.push(tag);
21880             
21881         }, this);
21882         
21883         
21884         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
21885             if (w.indexOf(tag) > -1) {
21886                 return;
21887             }
21888             this.black.push(tag);
21889             
21890         }, this);
21891         
21892         Roo.each(b, function(tag) {
21893             if (w.indexOf(tag) > -1) {
21894                 return;
21895             }
21896             if (this.black.indexOf(tag) > -1) {
21897                 return;
21898             }
21899             this.black.push(tag);
21900             
21901         }, this);
21902         
21903         
21904         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
21905         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
21906         
21907         this.cwhite = [];
21908         this.cblack = [];
21909         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
21910             if (b.indexOf(tag) > -1) {
21911                 return;
21912             }
21913             this.cwhite.push(tag);
21914             
21915         }, this);
21916         
21917         Roo.each(w, function(tag) {
21918             if (b.indexOf(tag) > -1) {
21919                 return;
21920             }
21921             if (this.cwhite.indexOf(tag) > -1) {
21922                 return;
21923             }
21924             this.cwhite.push(tag);
21925             
21926         }, this);
21927         
21928         
21929         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
21930             if (w.indexOf(tag) > -1) {
21931                 return;
21932             }
21933             this.cblack.push(tag);
21934             
21935         }, this);
21936         
21937         Roo.each(b, function(tag) {
21938             if (w.indexOf(tag) > -1) {
21939                 return;
21940             }
21941             if (this.cblack.indexOf(tag) > -1) {
21942                 return;
21943             }
21944             this.cblack.push(tag);
21945             
21946         }, this);
21947     },
21948     
21949     setStylesheets : function(stylesheets)
21950     {
21951         if(typeof(stylesheets) == 'string'){
21952             Roo.get(this.iframe.contentDocument.head).createChild({
21953                 tag : 'link',
21954                 rel : 'stylesheet',
21955                 type : 'text/css',
21956                 href : stylesheets
21957             });
21958             
21959             return;
21960         }
21961         var _this = this;
21962      
21963         Roo.each(stylesheets, function(s) {
21964             if(!s.length){
21965                 return;
21966             }
21967             
21968             Roo.get(_this.iframe.contentDocument.head).createChild({
21969                 tag : 'link',
21970                 rel : 'stylesheet',
21971                 type : 'text/css',
21972                 href : s
21973             });
21974         });
21975
21976         
21977     },
21978     
21979     removeStylesheets : function()
21980     {
21981         var _this = this;
21982         
21983         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
21984             s.remove();
21985         });
21986     },
21987     
21988     setStyle : function(style)
21989     {
21990         Roo.get(this.iframe.contentDocument.head).createChild({
21991             tag : 'style',
21992             type : 'text/css',
21993             html : style
21994         });
21995
21996         return;
21997     }
21998     
21999     // hide stuff that is not compatible
22000     /**
22001      * @event blur
22002      * @hide
22003      */
22004     /**
22005      * @event change
22006      * @hide
22007      */
22008     /**
22009      * @event focus
22010      * @hide
22011      */
22012     /**
22013      * @event specialkey
22014      * @hide
22015      */
22016     /**
22017      * @cfg {String} fieldClass @hide
22018      */
22019     /**
22020      * @cfg {String} focusClass @hide
22021      */
22022     /**
22023      * @cfg {String} autoCreate @hide
22024      */
22025     /**
22026      * @cfg {String} inputType @hide
22027      */
22028     /**
22029      * @cfg {String} invalidClass @hide
22030      */
22031     /**
22032      * @cfg {String} invalidText @hide
22033      */
22034     /**
22035      * @cfg {String} msgFx @hide
22036      */
22037     /**
22038      * @cfg {String} validateOnBlur @hide
22039      */
22040 });
22041
22042 Roo.HtmlEditorCore.white = [
22043         'area', 'br', 'img', 'input', 'hr', 'wbr',
22044         
22045        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
22046        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
22047        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
22048        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
22049        'table',   'ul',         'xmp', 
22050        
22051        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
22052       'thead',   'tr', 
22053      
22054       'dir', 'menu', 'ol', 'ul', 'dl',
22055        
22056       'embed',  'object'
22057 ];
22058
22059
22060 Roo.HtmlEditorCore.black = [
22061     //    'embed',  'object', // enable - backend responsiblity to clean thiese
22062         'applet', // 
22063         'base',   'basefont', 'bgsound', 'blink',  'body', 
22064         'frame',  'frameset', 'head',    'html',   'ilayer', 
22065         'iframe', 'layer',  'link',     'meta',    'object',   
22066         'script', 'style' ,'title',  'xml' // clean later..
22067 ];
22068 Roo.HtmlEditorCore.clean = [
22069     'script', 'style', 'title', 'xml'
22070 ];
22071 Roo.HtmlEditorCore.remove = [
22072     'font'
22073 ];
22074 // attributes..
22075
22076 Roo.HtmlEditorCore.ablack = [
22077     'on'
22078 ];
22079     
22080 Roo.HtmlEditorCore.aclean = [ 
22081     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22082 ];
22083
22084 // protocols..
22085 Roo.HtmlEditorCore.pwhite= [
22086         'http',  'https',  'mailto'
22087 ];
22088
22089 // white listed style attributes.
22090 Roo.HtmlEditorCore.cwhite= [
22091       //  'text-align', /// default is to allow most things..
22092       
22093          
22094 //        'font-size'//??
22095 ];
22096
22097 // black listed style attributes.
22098 Roo.HtmlEditorCore.cblack= [
22099       //  'font-size' -- this can be set by the project 
22100 ];
22101
22102
22103 Roo.HtmlEditorCore.swapCodes   =[ 
22104     [    8211, "--" ], 
22105     [    8212, "--" ], 
22106     [    8216,  "'" ],  
22107     [    8217, "'" ],  
22108     [    8220, '"' ],  
22109     [    8221, '"' ],  
22110     [    8226, "*" ],  
22111     [    8230, "..." ]
22112 ]; 
22113
22114     //<script type="text/javascript">
22115
22116 /*
22117  * Ext JS Library 1.1.1
22118  * Copyright(c) 2006-2007, Ext JS, LLC.
22119  * Licence LGPL
22120  * 
22121  */
22122  
22123  
22124 Roo.form.HtmlEditor = function(config){
22125     
22126     
22127     
22128     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
22129     
22130     if (!this.toolbars) {
22131         this.toolbars = [];
22132     }
22133     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22134     
22135     
22136 };
22137
22138 /**
22139  * @class Roo.form.HtmlEditor
22140  * @extends Roo.form.Field
22141  * Provides a lightweight HTML Editor component.
22142  *
22143  * This has been tested on Fireforx / Chrome.. IE may not be so great..
22144  * 
22145  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
22146  * supported by this editor.</b><br/><br/>
22147  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
22148  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22149  */
22150 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
22151     /**
22152      * @cfg {Boolean} clearUp
22153      */
22154     clearUp : true,
22155       /**
22156      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22157      */
22158     toolbars : false,
22159    
22160      /**
22161      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22162      *                        Roo.resizable.
22163      */
22164     resizable : false,
22165      /**
22166      * @cfg {Number} height (in pixels)
22167      */   
22168     height: 300,
22169    /**
22170      * @cfg {Number} width (in pixels)
22171      */   
22172     width: 500,
22173     
22174     /**
22175      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22176      * 
22177      */
22178     stylesheets: false,
22179     
22180     
22181      /**
22182      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
22183      * 
22184      */
22185     cblack: false,
22186     /**
22187      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
22188      * 
22189      */
22190     cwhite: false,
22191     
22192      /**
22193      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
22194      * 
22195      */
22196     black: false,
22197     /**
22198      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
22199      * 
22200      */
22201     white: false,
22202     
22203     // id of frame..
22204     frameId: false,
22205     
22206     // private properties
22207     validationEvent : false,
22208     deferHeight: true,
22209     initialized : false,
22210     activated : false,
22211     
22212     onFocus : Roo.emptyFn,
22213     iframePad:3,
22214     hideMode:'offsets',
22215     
22216     actionMode : 'container', // defaults to hiding it...
22217     
22218     defaultAutoCreate : { // modified by initCompnoent..
22219         tag: "textarea",
22220         style:"width:500px;height:300px;",
22221         autocomplete: "new-password"
22222     },
22223
22224     // private
22225     initComponent : function(){
22226         this.addEvents({
22227             /**
22228              * @event initialize
22229              * Fires when the editor is fully initialized (including the iframe)
22230              * @param {HtmlEditor} this
22231              */
22232             initialize: true,
22233             /**
22234              * @event activate
22235              * Fires when the editor is first receives the focus. Any insertion must wait
22236              * until after this event.
22237              * @param {HtmlEditor} this
22238              */
22239             activate: true,
22240              /**
22241              * @event beforesync
22242              * Fires before the textarea is updated with content from the editor iframe. Return false
22243              * to cancel the sync.
22244              * @param {HtmlEditor} this
22245              * @param {String} html
22246              */
22247             beforesync: true,
22248              /**
22249              * @event beforepush
22250              * Fires before the iframe editor is updated with content from the textarea. Return false
22251              * to cancel the push.
22252              * @param {HtmlEditor} this
22253              * @param {String} html
22254              */
22255             beforepush: true,
22256              /**
22257              * @event sync
22258              * Fires when the textarea is updated with content from the editor iframe.
22259              * @param {HtmlEditor} this
22260              * @param {String} html
22261              */
22262             sync: true,
22263              /**
22264              * @event push
22265              * Fires when the iframe editor is updated with content from the textarea.
22266              * @param {HtmlEditor} this
22267              * @param {String} html
22268              */
22269             push: true,
22270              /**
22271              * @event editmodechange
22272              * Fires when the editor switches edit modes
22273              * @param {HtmlEditor} this
22274              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22275              */
22276             editmodechange: true,
22277             /**
22278              * @event editorevent
22279              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22280              * @param {HtmlEditor} this
22281              */
22282             editorevent: true,
22283             /**
22284              * @event firstfocus
22285              * Fires when on first focus - needed by toolbars..
22286              * @param {HtmlEditor} this
22287              */
22288             firstfocus: true,
22289             /**
22290              * @event autosave
22291              * Auto save the htmlEditor value as a file into Events
22292              * @param {HtmlEditor} this
22293              */
22294             autosave: true,
22295             /**
22296              * @event savedpreview
22297              * preview the saved version of htmlEditor
22298              * @param {HtmlEditor} this
22299              */
22300             savedpreview: true,
22301             
22302             /**
22303             * @event stylesheetsclick
22304             * Fires when press the Sytlesheets button
22305             * @param {Roo.HtmlEditorCore} this
22306             */
22307             stylesheetsclick: true
22308         });
22309         this.defaultAutoCreate =  {
22310             tag: "textarea",
22311             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
22312             autocomplete: "new-password"
22313         };
22314     },
22315
22316     /**
22317      * Protected method that will not generally be called directly. It
22318      * is called when the editor creates its toolbar. Override this method if you need to
22319      * add custom toolbar buttons.
22320      * @param {HtmlEditor} editor
22321      */
22322     createToolbar : function(editor){
22323         Roo.log("create toolbars");
22324         if (!editor.toolbars || !editor.toolbars.length) {
22325             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
22326         }
22327         
22328         for (var i =0 ; i < editor.toolbars.length;i++) {
22329             editor.toolbars[i] = Roo.factory(
22330                     typeof(editor.toolbars[i]) == 'string' ?
22331                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
22332                 Roo.form.HtmlEditor);
22333             editor.toolbars[i].init(editor);
22334         }
22335          
22336         
22337     },
22338
22339      
22340     // private
22341     onRender : function(ct, position)
22342     {
22343         var _t = this;
22344         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
22345         
22346         this.wrap = this.el.wrap({
22347             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22348         });
22349         
22350         this.editorcore.onRender(ct, position);
22351          
22352         if (this.resizable) {
22353             this.resizeEl = new Roo.Resizable(this.wrap, {
22354                 pinned : true,
22355                 wrap: true,
22356                 dynamic : true,
22357                 minHeight : this.height,
22358                 height: this.height,
22359                 handles : this.resizable,
22360                 width: this.width,
22361                 listeners : {
22362                     resize : function(r, w, h) {
22363                         _t.onResize(w,h); // -something
22364                     }
22365                 }
22366             });
22367             
22368         }
22369         this.createToolbar(this);
22370        
22371         
22372         if(!this.width){
22373             this.setSize(this.wrap.getSize());
22374         }
22375         if (this.resizeEl) {
22376             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22377             // should trigger onReize..
22378         }
22379         
22380         this.keyNav = new Roo.KeyNav(this.el, {
22381             
22382             "tab" : function(e){
22383                 e.preventDefault();
22384                 
22385                 var value = this.getValue();
22386                 
22387                 var start = this.el.dom.selectionStart;
22388                 var end = this.el.dom.selectionEnd;
22389                 
22390                 if(!e.shiftKey){
22391                     
22392                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
22393                     this.el.dom.setSelectionRange(end + 1, end + 1);
22394                     return;
22395                 }
22396                 
22397                 var f = value.substring(0, start).split("\t");
22398                 
22399                 if(f.pop().length != 0){
22400                     return;
22401                 }
22402                 
22403                 this.setValue(f.join("\t") + value.substring(end));
22404                 this.el.dom.setSelectionRange(start - 1, start - 1);
22405                 
22406             },
22407             
22408             "home" : function(e){
22409                 e.preventDefault();
22410                 
22411                 var curr = this.el.dom.selectionStart;
22412                 var lines = this.getValue().split("\n");
22413                 
22414                 if(!lines.length){
22415                     return;
22416                 }
22417                 
22418                 if(e.ctrlKey){
22419                     this.el.dom.setSelectionRange(0, 0);
22420                     return;
22421                 }
22422                 
22423                 var pos = 0;
22424                 
22425                 for (var i = 0; i < lines.length;i++) {
22426                     pos += lines[i].length;
22427                     
22428                     if(i != 0){
22429                         pos += 1;
22430                     }
22431                     
22432                     if(pos < curr){
22433                         continue;
22434                     }
22435                     
22436                     pos -= lines[i].length;
22437                     
22438                     break;
22439                 }
22440                 
22441                 if(!e.shiftKey){
22442                     this.el.dom.setSelectionRange(pos, pos);
22443                     return;
22444                 }
22445                 
22446                 this.el.dom.selectionStart = pos;
22447                 this.el.dom.selectionEnd = curr;
22448             },
22449             
22450             "end" : function(e){
22451                 e.preventDefault();
22452                 
22453                 var curr = this.el.dom.selectionStart;
22454                 var lines = this.getValue().split("\n");
22455                 
22456                 if(!lines.length){
22457                     return;
22458                 }
22459                 
22460                 if(e.ctrlKey){
22461                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
22462                     return;
22463                 }
22464                 
22465                 var pos = 0;
22466                 
22467                 for (var i = 0; i < lines.length;i++) {
22468                     
22469                     pos += lines[i].length;
22470                     
22471                     if(i != 0){
22472                         pos += 1;
22473                     }
22474                     
22475                     if(pos < curr){
22476                         continue;
22477                     }
22478                     
22479                     break;
22480                 }
22481                 
22482                 if(!e.shiftKey){
22483                     this.el.dom.setSelectionRange(pos, pos);
22484                     return;
22485                 }
22486                 
22487                 this.el.dom.selectionStart = curr;
22488                 this.el.dom.selectionEnd = pos;
22489             },
22490
22491             scope : this,
22492
22493             doRelay : function(foo, bar, hname){
22494                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
22495             },
22496
22497             forceKeyDown: true
22498         });
22499         
22500 //        if(this.autosave && this.w){
22501 //            this.autoSaveFn = setInterval(this.autosave, 1000);
22502 //        }
22503     },
22504
22505     // private
22506     onResize : function(w, h)
22507     {
22508         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
22509         var ew = false;
22510         var eh = false;
22511         
22512         if(this.el ){
22513             if(typeof w == 'number'){
22514                 var aw = w - this.wrap.getFrameWidth('lr');
22515                 this.el.setWidth(this.adjustWidth('textarea', aw));
22516                 ew = aw;
22517             }
22518             if(typeof h == 'number'){
22519                 var tbh = 0;
22520                 for (var i =0; i < this.toolbars.length;i++) {
22521                     // fixme - ask toolbars for heights?
22522                     tbh += this.toolbars[i].tb.el.getHeight();
22523                     if (this.toolbars[i].footer) {
22524                         tbh += this.toolbars[i].footer.el.getHeight();
22525                     }
22526                 }
22527                 
22528                 
22529                 
22530                 
22531                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
22532                 ah -= 5; // knock a few pixes off for look..
22533 //                Roo.log(ah);
22534                 this.el.setHeight(this.adjustWidth('textarea', ah));
22535                 var eh = ah;
22536             }
22537         }
22538         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
22539         this.editorcore.onResize(ew,eh);
22540         
22541     },
22542
22543     /**
22544      * Toggles the editor between standard and source edit mode.
22545      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22546      */
22547     toggleSourceEdit : function(sourceEditMode)
22548     {
22549         this.editorcore.toggleSourceEdit(sourceEditMode);
22550         
22551         if(this.editorcore.sourceEditMode){
22552             Roo.log('editor - showing textarea');
22553             
22554 //            Roo.log('in');
22555 //            Roo.log(this.syncValue());
22556             this.editorcore.syncValue();
22557             this.el.removeClass('x-hidden');
22558             this.el.dom.removeAttribute('tabIndex');
22559             this.el.focus();
22560             
22561             for (var i = 0; i < this.toolbars.length; i++) {
22562                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
22563                     this.toolbars[i].tb.hide();
22564                     this.toolbars[i].footer.hide();
22565                 }
22566             }
22567             
22568         }else{
22569             Roo.log('editor - hiding textarea');
22570 //            Roo.log('out')
22571 //            Roo.log(this.pushValue()); 
22572             this.editorcore.pushValue();
22573             
22574             this.el.addClass('x-hidden');
22575             this.el.dom.setAttribute('tabIndex', -1);
22576             
22577             for (var i = 0; i < this.toolbars.length; i++) {
22578                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
22579                     this.toolbars[i].tb.show();
22580                     this.toolbars[i].footer.show();
22581                 }
22582             }
22583             
22584             //this.deferFocus();
22585         }
22586         
22587         this.setSize(this.wrap.getSize());
22588         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
22589         
22590         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
22591     },
22592  
22593     // private (for BoxComponent)
22594     adjustSize : Roo.BoxComponent.prototype.adjustSize,
22595
22596     // private (for BoxComponent)
22597     getResizeEl : function(){
22598         return this.wrap;
22599     },
22600
22601     // private (for BoxComponent)
22602     getPositionEl : function(){
22603         return this.wrap;
22604     },
22605
22606     // private
22607     initEvents : function(){
22608         this.originalValue = this.getValue();
22609     },
22610
22611     /**
22612      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22613      * @method
22614      */
22615     markInvalid : Roo.emptyFn,
22616     /**
22617      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22618      * @method
22619      */
22620     clearInvalid : Roo.emptyFn,
22621
22622     setValue : function(v){
22623         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
22624         this.editorcore.pushValue();
22625     },
22626
22627      
22628     // private
22629     deferFocus : function(){
22630         this.focus.defer(10, this);
22631     },
22632
22633     // doc'ed in Field
22634     focus : function(){
22635         this.editorcore.focus();
22636         
22637     },
22638       
22639
22640     // private
22641     onDestroy : function(){
22642         
22643         
22644         
22645         if(this.rendered){
22646             
22647             for (var i =0; i < this.toolbars.length;i++) {
22648                 // fixme - ask toolbars for heights?
22649                 this.toolbars[i].onDestroy();
22650             }
22651             
22652             this.wrap.dom.innerHTML = '';
22653             this.wrap.remove();
22654         }
22655     },
22656
22657     // private
22658     onFirstFocus : function(){
22659         //Roo.log("onFirstFocus");
22660         this.editorcore.onFirstFocus();
22661          for (var i =0; i < this.toolbars.length;i++) {
22662             this.toolbars[i].onFirstFocus();
22663         }
22664         
22665     },
22666     
22667     // private
22668     syncValue : function()
22669     {
22670         this.editorcore.syncValue();
22671     },
22672     
22673     pushValue : function()
22674     {
22675         this.editorcore.pushValue();
22676     },
22677     
22678     setStylesheets : function(stylesheets)
22679     {
22680         this.editorcore.setStylesheets(stylesheets);
22681     },
22682     
22683     removeStylesheets : function()
22684     {
22685         this.editorcore.removeStylesheets();
22686     }
22687      
22688     
22689     // hide stuff that is not compatible
22690     /**
22691      * @event blur
22692      * @hide
22693      */
22694     /**
22695      * @event change
22696      * @hide
22697      */
22698     /**
22699      * @event focus
22700      * @hide
22701      */
22702     /**
22703      * @event specialkey
22704      * @hide
22705      */
22706     /**
22707      * @cfg {String} fieldClass @hide
22708      */
22709     /**
22710      * @cfg {String} focusClass @hide
22711      */
22712     /**
22713      * @cfg {String} autoCreate @hide
22714      */
22715     /**
22716      * @cfg {String} inputType @hide
22717      */
22718     /**
22719      * @cfg {String} invalidClass @hide
22720      */
22721     /**
22722      * @cfg {String} invalidText @hide
22723      */
22724     /**
22725      * @cfg {String} msgFx @hide
22726      */
22727     /**
22728      * @cfg {String} validateOnBlur @hide
22729      */
22730 });
22731  
22732     // <script type="text/javascript">
22733 /*
22734  * Based on
22735  * Ext JS Library 1.1.1
22736  * Copyright(c) 2006-2007, Ext JS, LLC.
22737  *  
22738  
22739  */
22740
22741 /**
22742  * @class Roo.form.HtmlEditorToolbar1
22743  * Basic Toolbar
22744  * 
22745  * Usage:
22746  *
22747  new Roo.form.HtmlEditor({
22748     ....
22749     toolbars : [
22750         new Roo.form.HtmlEditorToolbar1({
22751             disable : { fonts: 1 , format: 1, ..., ... , ...],
22752             btns : [ .... ]
22753         })
22754     }
22755      
22756  * 
22757  * @cfg {Object} disable List of elements to disable..
22758  * @cfg {Array} btns List of additional buttons.
22759  * 
22760  * 
22761  * NEEDS Extra CSS? 
22762  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22763  */
22764  
22765 Roo.form.HtmlEditor.ToolbarStandard = function(config)
22766 {
22767     
22768     Roo.apply(this, config);
22769     
22770     // default disabled, based on 'good practice'..
22771     this.disable = this.disable || {};
22772     Roo.applyIf(this.disable, {
22773         fontSize : true,
22774         colors : true,
22775         specialElements : true
22776     });
22777     
22778     
22779     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22780     // dont call parent... till later.
22781 }
22782
22783 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
22784     
22785     tb: false,
22786     
22787     rendered: false,
22788     
22789     editor : false,
22790     editorcore : false,
22791     /**
22792      * @cfg {Object} disable  List of toolbar elements to disable
22793          
22794      */
22795     disable : false,
22796     
22797     
22798      /**
22799      * @cfg {String} createLinkText The default text for the create link prompt
22800      */
22801     createLinkText : 'Please enter the URL for the link:',
22802     /**
22803      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
22804      */
22805     defaultLinkValue : 'http:/'+'/',
22806    
22807     
22808       /**
22809      * @cfg {Array} fontFamilies An array of available font families
22810      */
22811     fontFamilies : [
22812         'Arial',
22813         'Courier New',
22814         'Tahoma',
22815         'Times New Roman',
22816         'Verdana'
22817     ],
22818     
22819     specialChars : [
22820            "&#169;",
22821           "&#174;",     
22822           "&#8482;",    
22823           "&#163;" ,    
22824          // "&#8212;",    
22825           "&#8230;",    
22826           "&#247;" ,    
22827         //  "&#225;" ,     ?? a acute?
22828            "&#8364;"    , //Euro
22829        //   "&#8220;"    ,
22830         //  "&#8221;"    ,
22831         //  "&#8226;"    ,
22832           "&#176;"  //   , // degrees
22833
22834          // "&#233;"     , // e ecute
22835          // "&#250;"     , // u ecute?
22836     ],
22837     
22838     specialElements : [
22839         {
22840             text: "Insert Table",
22841             xtype: 'MenuItem',
22842             xns : Roo.Menu,
22843             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
22844                 
22845         },
22846         {    
22847             text: "Insert Image",
22848             xtype: 'MenuItem',
22849             xns : Roo.Menu,
22850             ihtml : '<img src="about:blank"/>'
22851             
22852         }
22853         
22854          
22855     ],
22856     
22857     
22858     inputElements : [ 
22859             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
22860             "input:submit", "input:button", "select", "textarea", "label" ],
22861     formats : [
22862         ["p"] ,  
22863         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
22864         ["pre"],[ "code"], 
22865         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
22866         ['div'],['span']
22867     ],
22868     
22869     cleanStyles : [
22870         "font-size"
22871     ],
22872      /**
22873      * @cfg {String} defaultFont default font to use.
22874      */
22875     defaultFont: 'tahoma',
22876    
22877     fontSelect : false,
22878     
22879     
22880     formatCombo : false,
22881     
22882     init : function(editor)
22883     {
22884         this.editor = editor;
22885         this.editorcore = editor.editorcore ? editor.editorcore : editor;
22886         var editorcore = this.editorcore;
22887         
22888         var _t = this;
22889         
22890         var fid = editorcore.frameId;
22891         var etb = this;
22892         function btn(id, toggle, handler){
22893             var xid = fid + '-'+ id ;
22894             return {
22895                 id : xid,
22896                 cmd : id,
22897                 cls : 'x-btn-icon x-edit-'+id,
22898                 enableToggle:toggle !== false,
22899                 scope: _t, // was editor...
22900                 handler:handler||_t.relayBtnCmd,
22901                 clickEvent:'mousedown',
22902                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
22903                 tabIndex:-1
22904             };
22905         }
22906         
22907         
22908         
22909         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
22910         this.tb = tb;
22911          // stop form submits
22912         tb.el.on('click', function(e){
22913             e.preventDefault(); // what does this do?
22914         });
22915
22916         if(!this.disable.font) { // && !Roo.isSafari){
22917             /* why no safari for fonts 
22918             editor.fontSelect = tb.el.createChild({
22919                 tag:'select',
22920                 tabIndex: -1,
22921                 cls:'x-font-select',
22922                 html: this.createFontOptions()
22923             });
22924             
22925             editor.fontSelect.on('change', function(){
22926                 var font = editor.fontSelect.dom.value;
22927                 editor.relayCmd('fontname', font);
22928                 editor.deferFocus();
22929             }, editor);
22930             
22931             tb.add(
22932                 editor.fontSelect.dom,
22933                 '-'
22934             );
22935             */
22936             
22937         };
22938         if(!this.disable.formats){
22939             this.formatCombo = new Roo.form.ComboBox({
22940                 store: new Roo.data.SimpleStore({
22941                     id : 'tag',
22942                     fields: ['tag'],
22943                     data : this.formats // from states.js
22944                 }),
22945                 blockFocus : true,
22946                 name : '',
22947                 //autoCreate : {tag: "div",  size: "20"},
22948                 displayField:'tag',
22949                 typeAhead: false,
22950                 mode: 'local',
22951                 editable : false,
22952                 triggerAction: 'all',
22953                 emptyText:'Add tag',
22954                 selectOnFocus:true,
22955                 width:135,
22956                 listeners : {
22957                     'select': function(c, r, i) {
22958                         editorcore.insertTag(r.get('tag'));
22959                         editor.focus();
22960                     }
22961                 }
22962
22963             });
22964             tb.addField(this.formatCombo);
22965             
22966         }
22967         
22968         if(!this.disable.format){
22969             tb.add(
22970                 btn('bold'),
22971                 btn('italic'),
22972                 btn('underline'),
22973                 btn('strikethrough')
22974             );
22975         };
22976         if(!this.disable.fontSize){
22977             tb.add(
22978                 '-',
22979                 
22980                 
22981                 btn('increasefontsize', false, editorcore.adjustFont),
22982                 btn('decreasefontsize', false, editorcore.adjustFont)
22983             );
22984         };
22985         
22986         
22987         if(!this.disable.colors){
22988             tb.add(
22989                 '-', {
22990                     id:editorcore.frameId +'-forecolor',
22991                     cls:'x-btn-icon x-edit-forecolor',
22992                     clickEvent:'mousedown',
22993                     tooltip: this.buttonTips['forecolor'] || undefined,
22994                     tabIndex:-1,
22995                     menu : new Roo.menu.ColorMenu({
22996                         allowReselect: true,
22997                         focus: Roo.emptyFn,
22998                         value:'000000',
22999                         plain:true,
23000                         selectHandler: function(cp, color){
23001                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
23002                             editor.deferFocus();
23003                         },
23004                         scope: editorcore,
23005                         clickEvent:'mousedown'
23006                     })
23007                 }, {
23008                     id:editorcore.frameId +'backcolor',
23009                     cls:'x-btn-icon x-edit-backcolor',
23010                     clickEvent:'mousedown',
23011                     tooltip: this.buttonTips['backcolor'] || undefined,
23012                     tabIndex:-1,
23013                     menu : new Roo.menu.ColorMenu({
23014                         focus: Roo.emptyFn,
23015                         value:'FFFFFF',
23016                         plain:true,
23017                         allowReselect: true,
23018                         selectHandler: function(cp, color){
23019                             if(Roo.isGecko){
23020                                 editorcore.execCmd('useCSS', false);
23021                                 editorcore.execCmd('hilitecolor', color);
23022                                 editorcore.execCmd('useCSS', true);
23023                                 editor.deferFocus();
23024                             }else{
23025                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
23026                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
23027                                 editor.deferFocus();
23028                             }
23029                         },
23030                         scope:editorcore,
23031                         clickEvent:'mousedown'
23032                     })
23033                 }
23034             );
23035         };
23036         // now add all the items...
23037         
23038
23039         if(!this.disable.alignments){
23040             tb.add(
23041                 '-',
23042                 btn('justifyleft'),
23043                 btn('justifycenter'),
23044                 btn('justifyright')
23045             );
23046         };
23047
23048         //if(!Roo.isSafari){
23049             if(!this.disable.links){
23050                 tb.add(
23051                     '-',
23052                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
23053                 );
23054             };
23055
23056             if(!this.disable.lists){
23057                 tb.add(
23058                     '-',
23059                     btn('insertorderedlist'),
23060                     btn('insertunorderedlist')
23061                 );
23062             }
23063             if(!this.disable.sourceEdit){
23064                 tb.add(
23065                     '-',
23066                     btn('sourceedit', true, function(btn){
23067                         this.toggleSourceEdit(btn.pressed);
23068                     })
23069                 );
23070             }
23071         //}
23072         
23073         var smenu = { };
23074         // special menu.. - needs to be tidied up..
23075         if (!this.disable.special) {
23076             smenu = {
23077                 text: "&#169;",
23078                 cls: 'x-edit-none',
23079                 
23080                 menu : {
23081                     items : []
23082                 }
23083             };
23084             for (var i =0; i < this.specialChars.length; i++) {
23085                 smenu.menu.items.push({
23086                     
23087                     html: this.specialChars[i],
23088                     handler: function(a,b) {
23089                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
23090                         //editor.insertAtCursor(a.html);
23091                         
23092                     },
23093                     tabIndex:-1
23094                 });
23095             }
23096             
23097             
23098             tb.add(smenu);
23099             
23100             
23101         }
23102         
23103         var cmenu = { };
23104         if (!this.disable.cleanStyles) {
23105             cmenu = {
23106                 cls: 'x-btn-icon x-btn-clear',
23107                 
23108                 menu : {
23109                     items : []
23110                 }
23111             };
23112             for (var i =0; i < this.cleanStyles.length; i++) {
23113                 cmenu.menu.items.push({
23114                     actiontype : this.cleanStyles[i],
23115                     html: 'Remove ' + this.cleanStyles[i],
23116                     handler: function(a,b) {
23117 //                        Roo.log(a);
23118 //                        Roo.log(b);
23119                         var c = Roo.get(editorcore.doc.body);
23120                         c.select('[style]').each(function(s) {
23121                             s.dom.style.removeProperty(a.actiontype);
23122                         });
23123                         editorcore.syncValue();
23124                     },
23125                     tabIndex:-1
23126                 });
23127             }
23128              cmenu.menu.items.push({
23129                 actiontype : 'tablewidths',
23130                 html: 'Remove Table Widths',
23131                 handler: function(a,b) {
23132                     editorcore.cleanTableWidths();
23133                     editorcore.syncValue();
23134                 },
23135                 tabIndex:-1
23136             });
23137             cmenu.menu.items.push({
23138                 actiontype : 'word',
23139                 html: 'Remove MS Word Formating',
23140                 handler: function(a,b) {
23141                     editorcore.cleanWord();
23142                     editorcore.syncValue();
23143                 },
23144                 tabIndex:-1
23145             });
23146             
23147             cmenu.menu.items.push({
23148                 actiontype : 'all',
23149                 html: 'Remove All Styles',
23150                 handler: function(a,b) {
23151                     
23152                     var c = Roo.get(editorcore.doc.body);
23153                     c.select('[style]').each(function(s) {
23154                         s.dom.removeAttribute('style');
23155                     });
23156                     editorcore.syncValue();
23157                 },
23158                 tabIndex:-1
23159             });
23160             
23161             cmenu.menu.items.push({
23162                 actiontype : 'all',
23163                 html: 'Remove All CSS Classes',
23164                 handler: function(a,b) {
23165                     
23166                     var c = Roo.get(editorcore.doc.body);
23167                     c.select('[class]').each(function(s) {
23168                         s.dom.className = '';
23169                     });
23170                     editorcore.syncValue();
23171                 },
23172                 tabIndex:-1
23173             });
23174             
23175              cmenu.menu.items.push({
23176                 actiontype : 'tidy',
23177                 html: 'Tidy HTML Source',
23178                 handler: function(a,b) {
23179                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
23180                     editorcore.syncValue();
23181                 },
23182                 tabIndex:-1
23183             });
23184             
23185             
23186             tb.add(cmenu);
23187         }
23188          
23189         if (!this.disable.specialElements) {
23190             var semenu = {
23191                 text: "Other;",
23192                 cls: 'x-edit-none',
23193                 menu : {
23194                     items : []
23195                 }
23196             };
23197             for (var i =0; i < this.specialElements.length; i++) {
23198                 semenu.menu.items.push(
23199                     Roo.apply({ 
23200                         handler: function(a,b) {
23201                             editor.insertAtCursor(this.ihtml);
23202                         }
23203                     }, this.specialElements[i])
23204                 );
23205                     
23206             }
23207             
23208             tb.add(semenu);
23209             
23210             
23211         }
23212          
23213         
23214         if (this.btns) {
23215             for(var i =0; i< this.btns.length;i++) {
23216                 var b = Roo.factory(this.btns[i],Roo.form);
23217                 b.cls =  'x-edit-none';
23218                 
23219                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
23220                     b.cls += ' x-init-enable';
23221                 }
23222                 
23223                 b.scope = editorcore;
23224                 tb.add(b);
23225             }
23226         
23227         }
23228         
23229         
23230         
23231         // disable everything...
23232         
23233         this.tb.items.each(function(item){
23234             
23235            if(
23236                 item.id != editorcore.frameId+ '-sourceedit' && 
23237                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
23238             ){
23239                 
23240                 item.disable();
23241             }
23242         });
23243         this.rendered = true;
23244         
23245         // the all the btns;
23246         editor.on('editorevent', this.updateToolbar, this);
23247         // other toolbars need to implement this..
23248         //editor.on('editmodechange', this.updateToolbar, this);
23249     },
23250     
23251     
23252     relayBtnCmd : function(btn) {
23253         this.editorcore.relayCmd(btn.cmd);
23254     },
23255     // private used internally
23256     createLink : function(){
23257         Roo.log("create link?");
23258         var url = prompt(this.createLinkText, this.defaultLinkValue);
23259         if(url && url != 'http:/'+'/'){
23260             this.editorcore.relayCmd('createlink', url);
23261         }
23262     },
23263
23264     
23265     /**
23266      * Protected method that will not generally be called directly. It triggers
23267      * a toolbar update by reading the markup state of the current selection in the editor.
23268      */
23269     updateToolbar: function(){
23270
23271         if(!this.editorcore.activated){
23272             this.editor.onFirstFocus();
23273             return;
23274         }
23275
23276         var btns = this.tb.items.map, 
23277             doc = this.editorcore.doc,
23278             frameId = this.editorcore.frameId;
23279
23280         if(!this.disable.font && !Roo.isSafari){
23281             /*
23282             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
23283             if(name != this.fontSelect.dom.value){
23284                 this.fontSelect.dom.value = name;
23285             }
23286             */
23287         }
23288         if(!this.disable.format){
23289             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
23290             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
23291             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
23292             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
23293         }
23294         if(!this.disable.alignments){
23295             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
23296             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
23297             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
23298         }
23299         if(!Roo.isSafari && !this.disable.lists){
23300             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
23301             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
23302         }
23303         
23304         var ans = this.editorcore.getAllAncestors();
23305         if (this.formatCombo) {
23306             
23307             
23308             var store = this.formatCombo.store;
23309             this.formatCombo.setValue("");
23310             for (var i =0; i < ans.length;i++) {
23311                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23312                     // select it..
23313                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23314                     break;
23315                 }
23316             }
23317         }
23318         
23319         
23320         
23321         // hides menus... - so this cant be on a menu...
23322         Roo.menu.MenuMgr.hideAll();
23323
23324         //this.editorsyncValue();
23325     },
23326    
23327     
23328     createFontOptions : function(){
23329         var buf = [], fs = this.fontFamilies, ff, lc;
23330         
23331         
23332         
23333         for(var i = 0, len = fs.length; i< len; i++){
23334             ff = fs[i];
23335             lc = ff.toLowerCase();
23336             buf.push(
23337                 '<option value="',lc,'" style="font-family:',ff,';"',
23338                     (this.defaultFont == lc ? ' selected="true">' : '>'),
23339                     ff,
23340                 '</option>'
23341             );
23342         }
23343         return buf.join('');
23344     },
23345     
23346     toggleSourceEdit : function(sourceEditMode){
23347         
23348         Roo.log("toolbar toogle");
23349         if(sourceEditMode === undefined){
23350             sourceEditMode = !this.sourceEditMode;
23351         }
23352         this.sourceEditMode = sourceEditMode === true;
23353         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
23354         // just toggle the button?
23355         if(btn.pressed !== this.sourceEditMode){
23356             btn.toggle(this.sourceEditMode);
23357             return;
23358         }
23359         
23360         if(sourceEditMode){
23361             Roo.log("disabling buttons");
23362             this.tb.items.each(function(item){
23363                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
23364                     item.disable();
23365                 }
23366             });
23367           
23368         }else{
23369             Roo.log("enabling buttons");
23370             if(this.editorcore.initialized){
23371                 this.tb.items.each(function(item){
23372                     item.enable();
23373                 });
23374             }
23375             
23376         }
23377         Roo.log("calling toggole on editor");
23378         // tell the editor that it's been pressed..
23379         this.editor.toggleSourceEdit(sourceEditMode);
23380        
23381     },
23382      /**
23383      * Object collection of toolbar tooltips for the buttons in the editor. The key
23384      * is the command id associated with that button and the value is a valid QuickTips object.
23385      * For example:
23386 <pre><code>
23387 {
23388     bold : {
23389         title: 'Bold (Ctrl+B)',
23390         text: 'Make the selected text bold.',
23391         cls: 'x-html-editor-tip'
23392     },
23393     italic : {
23394         title: 'Italic (Ctrl+I)',
23395         text: 'Make the selected text italic.',
23396         cls: 'x-html-editor-tip'
23397     },
23398     ...
23399 </code></pre>
23400     * @type Object
23401      */
23402     buttonTips : {
23403         bold : {
23404             title: 'Bold (Ctrl+B)',
23405             text: 'Make the selected text bold.',
23406             cls: 'x-html-editor-tip'
23407         },
23408         italic : {
23409             title: 'Italic (Ctrl+I)',
23410             text: 'Make the selected text italic.',
23411             cls: 'x-html-editor-tip'
23412         },
23413         underline : {
23414             title: 'Underline (Ctrl+U)',
23415             text: 'Underline the selected text.',
23416             cls: 'x-html-editor-tip'
23417         },
23418         strikethrough : {
23419             title: 'Strikethrough',
23420             text: 'Strikethrough the selected text.',
23421             cls: 'x-html-editor-tip'
23422         },
23423         increasefontsize : {
23424             title: 'Grow Text',
23425             text: 'Increase the font size.',
23426             cls: 'x-html-editor-tip'
23427         },
23428         decreasefontsize : {
23429             title: 'Shrink Text',
23430             text: 'Decrease the font size.',
23431             cls: 'x-html-editor-tip'
23432         },
23433         backcolor : {
23434             title: 'Text Highlight Color',
23435             text: 'Change the background color of the selected text.',
23436             cls: 'x-html-editor-tip'
23437         },
23438         forecolor : {
23439             title: 'Font Color',
23440             text: 'Change the color of the selected text.',
23441             cls: 'x-html-editor-tip'
23442         },
23443         justifyleft : {
23444             title: 'Align Text Left',
23445             text: 'Align text to the left.',
23446             cls: 'x-html-editor-tip'
23447         },
23448         justifycenter : {
23449             title: 'Center Text',
23450             text: 'Center text in the editor.',
23451             cls: 'x-html-editor-tip'
23452         },
23453         justifyright : {
23454             title: 'Align Text Right',
23455             text: 'Align text to the right.',
23456             cls: 'x-html-editor-tip'
23457         },
23458         insertunorderedlist : {
23459             title: 'Bullet List',
23460             text: 'Start a bulleted list.',
23461             cls: 'x-html-editor-tip'
23462         },
23463         insertorderedlist : {
23464             title: 'Numbered List',
23465             text: 'Start a numbered list.',
23466             cls: 'x-html-editor-tip'
23467         },
23468         createlink : {
23469             title: 'Hyperlink',
23470             text: 'Make the selected text a hyperlink.',
23471             cls: 'x-html-editor-tip'
23472         },
23473         sourceedit : {
23474             title: 'Source Edit',
23475             text: 'Switch to source editing mode.',
23476             cls: 'x-html-editor-tip'
23477         }
23478     },
23479     // private
23480     onDestroy : function(){
23481         if(this.rendered){
23482             
23483             this.tb.items.each(function(item){
23484                 if(item.menu){
23485                     item.menu.removeAll();
23486                     if(item.menu.el){
23487                         item.menu.el.destroy();
23488                     }
23489                 }
23490                 item.destroy();
23491             });
23492              
23493         }
23494     },
23495     onFirstFocus: function() {
23496         this.tb.items.each(function(item){
23497            item.enable();
23498         });
23499     }
23500 });
23501
23502
23503
23504
23505 // <script type="text/javascript">
23506 /*
23507  * Based on
23508  * Ext JS Library 1.1.1
23509  * Copyright(c) 2006-2007, Ext JS, LLC.
23510  *  
23511  
23512  */
23513
23514  
23515 /**
23516  * @class Roo.form.HtmlEditor.ToolbarContext
23517  * Context Toolbar
23518  * 
23519  * Usage:
23520  *
23521  new Roo.form.HtmlEditor({
23522     ....
23523     toolbars : [
23524         { xtype: 'ToolbarStandard', styles : {} }
23525         { xtype: 'ToolbarContext', disable : {} }
23526     ]
23527 })
23528
23529      
23530  * 
23531  * @config : {Object} disable List of elements to disable.. (not done yet.)
23532  * @config : {Object} styles  Map of styles available.
23533  * 
23534  */
23535
23536 Roo.form.HtmlEditor.ToolbarContext = function(config)
23537 {
23538     
23539     Roo.apply(this, config);
23540     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23541     // dont call parent... till later.
23542     this.styles = this.styles || {};
23543 }
23544
23545  
23546
23547 Roo.form.HtmlEditor.ToolbarContext.types = {
23548     'IMG' : {
23549         width : {
23550             title: "Width",
23551             width: 40
23552         },
23553         height:  {
23554             title: "Height",
23555             width: 40
23556         },
23557         align: {
23558             title: "Align",
23559             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
23560             width : 80
23561             
23562         },
23563         border: {
23564             title: "Border",
23565             width: 40
23566         },
23567         alt: {
23568             title: "Alt",
23569             width: 120
23570         },
23571         src : {
23572             title: "Src",
23573             width: 220
23574         }
23575         
23576     },
23577     'A' : {
23578         name : {
23579             title: "Name",
23580             width: 50
23581         },
23582         target:  {
23583             title: "Target",
23584             width: 120
23585         },
23586         href:  {
23587             title: "Href",
23588             width: 220
23589         } // border?
23590         
23591     },
23592     'TABLE' : {
23593         rows : {
23594             title: "Rows",
23595             width: 20
23596         },
23597         cols : {
23598             title: "Cols",
23599             width: 20
23600         },
23601         width : {
23602             title: "Width",
23603             width: 40
23604         },
23605         height : {
23606             title: "Height",
23607             width: 40
23608         },
23609         border : {
23610             title: "Border",
23611             width: 20
23612         }
23613     },
23614     'TD' : {
23615         width : {
23616             title: "Width",
23617             width: 40
23618         },
23619         height : {
23620             title: "Height",
23621             width: 40
23622         },   
23623         align: {
23624             title: "Align",
23625             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
23626             width: 80
23627         },
23628         valign: {
23629             title: "Valign",
23630             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
23631             width: 80
23632         },
23633         colspan: {
23634             title: "Colspan",
23635             width: 20
23636             
23637         },
23638          'font-family'  : {
23639             title : "Font",
23640             style : 'fontFamily',
23641             displayField: 'display',
23642             optname : 'font-family',
23643             width: 140
23644         }
23645     },
23646     'INPUT' : {
23647         name : {
23648             title: "name",
23649             width: 120
23650         },
23651         value : {
23652             title: "Value",
23653             width: 120
23654         },
23655         width : {
23656             title: "Width",
23657             width: 40
23658         }
23659     },
23660     'LABEL' : {
23661         'for' : {
23662             title: "For",
23663             width: 120
23664         }
23665     },
23666     'TEXTAREA' : {
23667           name : {
23668             title: "name",
23669             width: 120
23670         },
23671         rows : {
23672             title: "Rows",
23673             width: 20
23674         },
23675         cols : {
23676             title: "Cols",
23677             width: 20
23678         }
23679     },
23680     'SELECT' : {
23681         name : {
23682             title: "name",
23683             width: 120
23684         },
23685         selectoptions : {
23686             title: "Options",
23687             width: 200
23688         }
23689     },
23690     
23691     // should we really allow this??
23692     // should this just be 
23693     'BODY' : {
23694         title : {
23695             title: "Title",
23696             width: 200,
23697             disabled : true
23698         }
23699     },
23700     'SPAN' : {
23701         'font-family'  : {
23702             title : "Font",
23703             style : 'fontFamily',
23704             displayField: 'display',
23705             optname : 'font-family',
23706             width: 140
23707         }
23708     },
23709     'DIV' : {
23710         'font-family'  : {
23711             title : "Font",
23712             style : 'fontFamily',
23713             displayField: 'display',
23714             optname : 'font-family',
23715             width: 140
23716         }
23717     },
23718      'P' : {
23719         'font-family'  : {
23720             title : "Font",
23721             style : 'fontFamily',
23722             displayField: 'display',
23723             optname : 'font-family',
23724             width: 140
23725         }
23726     },
23727     
23728     '*' : {
23729         // empty..
23730     }
23731
23732 };
23733
23734 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
23735 Roo.form.HtmlEditor.ToolbarContext.stores = false;
23736
23737 Roo.form.HtmlEditor.ToolbarContext.options = {
23738         'font-family'  : [ 
23739                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
23740                 [ 'Courier New', 'Courier New'],
23741                 [ 'Tahoma', 'Tahoma'],
23742                 [ 'Times New Roman,serif', 'Times'],
23743                 [ 'Verdana','Verdana' ]
23744         ]
23745 };
23746
23747 // fixme - these need to be configurable..
23748  
23749
23750 //Roo.form.HtmlEditor.ToolbarContext.types
23751
23752
23753 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
23754     
23755     tb: false,
23756     
23757     rendered: false,
23758     
23759     editor : false,
23760     editorcore : false,
23761     /**
23762      * @cfg {Object} disable  List of toolbar elements to disable
23763          
23764      */
23765     disable : false,
23766     /**
23767      * @cfg {Object} styles List of styles 
23768      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
23769      *
23770      * These must be defined in the page, so they get rendered correctly..
23771      * .headline { }
23772      * TD.underline { }
23773      * 
23774      */
23775     styles : false,
23776     
23777     options: false,
23778     
23779     toolbars : false,
23780     
23781     init : function(editor)
23782     {
23783         this.editor = editor;
23784         this.editorcore = editor.editorcore ? editor.editorcore : editor;
23785         var editorcore = this.editorcore;
23786         
23787         var fid = editorcore.frameId;
23788         var etb = this;
23789         function btn(id, toggle, handler){
23790             var xid = fid + '-'+ id ;
23791             return {
23792                 id : xid,
23793                 cmd : id,
23794                 cls : 'x-btn-icon x-edit-'+id,
23795                 enableToggle:toggle !== false,
23796                 scope: editorcore, // was editor...
23797                 handler:handler||editorcore.relayBtnCmd,
23798                 clickEvent:'mousedown',
23799                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
23800                 tabIndex:-1
23801             };
23802         }
23803         // create a new element.
23804         var wdiv = editor.wrap.createChild({
23805                 tag: 'div'
23806             }, editor.wrap.dom.firstChild.nextSibling, true);
23807         
23808         // can we do this more than once??
23809         
23810          // stop form submits
23811       
23812  
23813         // disable everything...
23814         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
23815         this.toolbars = {};
23816            
23817         for (var i in  ty) {
23818           
23819             this.toolbars[i] = this.buildToolbar(ty[i],i);
23820         }
23821         this.tb = this.toolbars.BODY;
23822         this.tb.el.show();
23823         this.buildFooter();
23824         this.footer.show();
23825         editor.on('hide', function( ) { this.footer.hide() }, this);
23826         editor.on('show', function( ) { this.footer.show() }, this);
23827         
23828          
23829         this.rendered = true;
23830         
23831         // the all the btns;
23832         editor.on('editorevent', this.updateToolbar, this);
23833         // other toolbars need to implement this..
23834         //editor.on('editmodechange', this.updateToolbar, this);
23835     },
23836     
23837     
23838     
23839     /**
23840      * Protected method that will not generally be called directly. It triggers
23841      * a toolbar update by reading the markup state of the current selection in the editor.
23842      *
23843      * Note you can force an update by calling on('editorevent', scope, false)
23844      */
23845     updateToolbar: function(editor,ev,sel){
23846
23847         //Roo.log(ev);
23848         // capture mouse up - this is handy for selecting images..
23849         // perhaps should go somewhere else...
23850         if(!this.editorcore.activated){
23851              this.editor.onFirstFocus();
23852             return;
23853         }
23854         
23855         
23856         
23857         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
23858         // selectNode - might want to handle IE?
23859         if (ev &&
23860             (ev.type == 'mouseup' || ev.type == 'click' ) &&
23861             ev.target && ev.target.tagName == 'IMG') {
23862             // they have click on an image...
23863             // let's see if we can change the selection...
23864             sel = ev.target;
23865          
23866               var nodeRange = sel.ownerDocument.createRange();
23867             try {
23868                 nodeRange.selectNode(sel);
23869             } catch (e) {
23870                 nodeRange.selectNodeContents(sel);
23871             }
23872             //nodeRange.collapse(true);
23873             var s = this.editorcore.win.getSelection();
23874             s.removeAllRanges();
23875             s.addRange(nodeRange);
23876         }  
23877         
23878       
23879         var updateFooter = sel ? false : true;
23880         
23881         
23882         var ans = this.editorcore.getAllAncestors();
23883         
23884         // pick
23885         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
23886         
23887         if (!sel) { 
23888             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
23889             sel = sel ? sel : this.editorcore.doc.body;
23890             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
23891             
23892         }
23893         // pick a menu that exists..
23894         var tn = sel.tagName.toUpperCase();
23895         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
23896         
23897         tn = sel.tagName.toUpperCase();
23898         
23899         var lastSel = this.tb.selectedNode;
23900         
23901         this.tb.selectedNode = sel;
23902         
23903         // if current menu does not match..
23904         
23905         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
23906                 
23907             this.tb.el.hide();
23908             ///console.log("show: " + tn);
23909             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
23910             this.tb.el.show();
23911             // update name
23912             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
23913             
23914             
23915             // update attributes
23916             if (this.tb.fields) {
23917                 this.tb.fields.each(function(e) {
23918                     if (e.stylename) {
23919                         e.setValue(sel.style[e.stylename]);
23920                         return;
23921                     } 
23922                    e.setValue(sel.getAttribute(e.attrname));
23923                 });
23924             }
23925             
23926             var hasStyles = false;
23927             for(var i in this.styles) {
23928                 hasStyles = true;
23929                 break;
23930             }
23931             
23932             // update styles
23933             if (hasStyles) { 
23934                 var st = this.tb.fields.item(0);
23935                 
23936                 st.store.removeAll();
23937                
23938                 
23939                 var cn = sel.className.split(/\s+/);
23940                 
23941                 var avs = [];
23942                 if (this.styles['*']) {
23943                     
23944                     Roo.each(this.styles['*'], function(v) {
23945                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
23946                     });
23947                 }
23948                 if (this.styles[tn]) { 
23949                     Roo.each(this.styles[tn], function(v) {
23950                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
23951                     });
23952                 }
23953                 
23954                 st.store.loadData(avs);
23955                 st.collapse();
23956                 st.setValue(cn);
23957             }
23958             // flag our selected Node.
23959             this.tb.selectedNode = sel;
23960            
23961            
23962             Roo.menu.MenuMgr.hideAll();
23963
23964         }
23965         
23966         if (!updateFooter) {
23967             //this.footDisp.dom.innerHTML = ''; 
23968             return;
23969         }
23970         // update the footer
23971         //
23972         var html = '';
23973         
23974         this.footerEls = ans.reverse();
23975         Roo.each(this.footerEls, function(a,i) {
23976             if (!a) { return; }
23977             html += html.length ? ' &gt; '  :  '';
23978             
23979             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
23980             
23981         });
23982        
23983         // 
23984         var sz = this.footDisp.up('td').getSize();
23985         this.footDisp.dom.style.width = (sz.width -10) + 'px';
23986         this.footDisp.dom.style.marginLeft = '5px';
23987         
23988         this.footDisp.dom.style.overflow = 'hidden';
23989         
23990         this.footDisp.dom.innerHTML = html;
23991             
23992         //this.editorsyncValue();
23993     },
23994      
23995     
23996    
23997        
23998     // private
23999     onDestroy : function(){
24000         if(this.rendered){
24001             
24002             this.tb.items.each(function(item){
24003                 if(item.menu){
24004                     item.menu.removeAll();
24005                     if(item.menu.el){
24006                         item.menu.el.destroy();
24007                     }
24008                 }
24009                 item.destroy();
24010             });
24011              
24012         }
24013     },
24014     onFirstFocus: function() {
24015         // need to do this for all the toolbars..
24016         this.tb.items.each(function(item){
24017            item.enable();
24018         });
24019     },
24020     buildToolbar: function(tlist, nm)
24021     {
24022         var editor = this.editor;
24023         var editorcore = this.editorcore;
24024          // create a new element.
24025         var wdiv = editor.wrap.createChild({
24026                 tag: 'div'
24027             }, editor.wrap.dom.firstChild.nextSibling, true);
24028         
24029        
24030         var tb = new Roo.Toolbar(wdiv);
24031         // add the name..
24032         
24033         tb.add(nm+ ":&nbsp;");
24034         
24035         var styles = [];
24036         for(var i in this.styles) {
24037             styles.push(i);
24038         }
24039         
24040         // styles...
24041         if (styles && styles.length) {
24042             
24043             // this needs a multi-select checkbox...
24044             tb.addField( new Roo.form.ComboBox({
24045                 store: new Roo.data.SimpleStore({
24046                     id : 'val',
24047                     fields: ['val', 'selected'],
24048                     data : [] 
24049                 }),
24050                 name : '-roo-edit-className',
24051                 attrname : 'className',
24052                 displayField: 'val',
24053                 typeAhead: false,
24054                 mode: 'local',
24055                 editable : false,
24056                 triggerAction: 'all',
24057                 emptyText:'Select Style',
24058                 selectOnFocus:true,
24059                 width: 130,
24060                 listeners : {
24061                     'select': function(c, r, i) {
24062                         // initial support only for on class per el..
24063                         tb.selectedNode.className =  r ? r.get('val') : '';
24064                         editorcore.syncValue();
24065                     }
24066                 }
24067     
24068             }));
24069         }
24070         
24071         var tbc = Roo.form.HtmlEditor.ToolbarContext;
24072         var tbops = tbc.options;
24073         
24074         for (var i in tlist) {
24075             
24076             var item = tlist[i];
24077             tb.add(item.title + ":&nbsp;");
24078             
24079             
24080             //optname == used so you can configure the options available..
24081             var opts = item.opts ? item.opts : false;
24082             if (item.optname) {
24083                 opts = tbops[item.optname];
24084            
24085             }
24086             
24087             if (opts) {
24088                 // opts == pulldown..
24089                 tb.addField( new Roo.form.ComboBox({
24090                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
24091                         id : 'val',
24092                         fields: ['val', 'display'],
24093                         data : opts  
24094                     }),
24095                     name : '-roo-edit-' + i,
24096                     attrname : i,
24097                     stylename : item.style ? item.style : false,
24098                     displayField: item.displayField ? item.displayField : 'val',
24099                     valueField :  'val',
24100                     typeAhead: false,
24101                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
24102                     editable : false,
24103                     triggerAction: 'all',
24104                     emptyText:'Select',
24105                     selectOnFocus:true,
24106                     width: item.width ? item.width  : 130,
24107                     listeners : {
24108                         'select': function(c, r, i) {
24109                             if (c.stylename) {
24110                                 tb.selectedNode.style[c.stylename] =  r.get('val');
24111                                 return;
24112                             }
24113                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
24114                         }
24115                     }
24116
24117                 }));
24118                 continue;
24119                     
24120                  
24121                 
24122                 tb.addField( new Roo.form.TextField({
24123                     name: i,
24124                     width: 100,
24125                     //allowBlank:false,
24126                     value: ''
24127                 }));
24128                 continue;
24129             }
24130             tb.addField( new Roo.form.TextField({
24131                 name: '-roo-edit-' + i,
24132                 attrname : i,
24133                 
24134                 width: item.width,
24135                 //allowBlank:true,
24136                 value: '',
24137                 listeners: {
24138                     'change' : function(f, nv, ov) {
24139                         tb.selectedNode.setAttribute(f.attrname, nv);
24140                         editorcore.syncValue();
24141                     }
24142                 }
24143             }));
24144              
24145         }
24146         
24147         var _this = this;
24148         
24149         if(nm == 'BODY'){
24150             tb.addSeparator();
24151         
24152             tb.addButton( {
24153                 text: 'Stylesheets',
24154
24155                 listeners : {
24156                     click : function ()
24157                     {
24158                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
24159                     }
24160                 }
24161             });
24162         }
24163         
24164         tb.addFill();
24165         tb.addButton( {
24166             text: 'Remove Tag',
24167     
24168             listeners : {
24169                 click : function ()
24170                 {
24171                     // remove
24172                     // undo does not work.
24173                      
24174                     var sn = tb.selectedNode;
24175                     
24176                     var pn = sn.parentNode;
24177                     
24178                     var stn =  sn.childNodes[0];
24179                     var en = sn.childNodes[sn.childNodes.length - 1 ];
24180                     while (sn.childNodes.length) {
24181                         var node = sn.childNodes[0];
24182                         sn.removeChild(node);
24183                         //Roo.log(node);
24184                         pn.insertBefore(node, sn);
24185                         
24186                     }
24187                     pn.removeChild(sn);
24188                     var range = editorcore.createRange();
24189         
24190                     range.setStart(stn,0);
24191                     range.setEnd(en,0); //????
24192                     //range.selectNode(sel);
24193                     
24194                     
24195                     var selection = editorcore.getSelection();
24196                     selection.removeAllRanges();
24197                     selection.addRange(range);
24198                     
24199                     
24200                     
24201                     //_this.updateToolbar(null, null, pn);
24202                     _this.updateToolbar(null, null, null);
24203                     _this.footDisp.dom.innerHTML = ''; 
24204                 }
24205             }
24206             
24207                     
24208                 
24209             
24210         });
24211         
24212         
24213         tb.el.on('click', function(e){
24214             e.preventDefault(); // what does this do?
24215         });
24216         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
24217         tb.el.hide();
24218         tb.name = nm;
24219         // dont need to disable them... as they will get hidden
24220         return tb;
24221          
24222         
24223     },
24224     buildFooter : function()
24225     {
24226         
24227         var fel = this.editor.wrap.createChild();
24228         this.footer = new Roo.Toolbar(fel);
24229         // toolbar has scrolly on left / right?
24230         var footDisp= new Roo.Toolbar.Fill();
24231         var _t = this;
24232         this.footer.add(
24233             {
24234                 text : '&lt;',
24235                 xtype: 'Button',
24236                 handler : function() {
24237                     _t.footDisp.scrollTo('left',0,true)
24238                 }
24239             }
24240         );
24241         this.footer.add( footDisp );
24242         this.footer.add( 
24243             {
24244                 text : '&gt;',
24245                 xtype: 'Button',
24246                 handler : function() {
24247                     // no animation..
24248                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
24249                 }
24250             }
24251         );
24252         var fel = Roo.get(footDisp.el);
24253         fel.addClass('x-editor-context');
24254         this.footDispWrap = fel; 
24255         this.footDispWrap.overflow  = 'hidden';
24256         
24257         this.footDisp = fel.createChild();
24258         this.footDispWrap.on('click', this.onContextClick, this)
24259         
24260         
24261     },
24262     onContextClick : function (ev,dom)
24263     {
24264         ev.preventDefault();
24265         var  cn = dom.className;
24266         //Roo.log(cn);
24267         if (!cn.match(/x-ed-loc-/)) {
24268             return;
24269         }
24270         var n = cn.split('-').pop();
24271         var ans = this.footerEls;
24272         var sel = ans[n];
24273         
24274          // pick
24275         var range = this.editorcore.createRange();
24276         
24277         range.selectNodeContents(sel);
24278         //range.selectNode(sel);
24279         
24280         
24281         var selection = this.editorcore.getSelection();
24282         selection.removeAllRanges();
24283         selection.addRange(range);
24284         
24285         
24286         
24287         this.updateToolbar(null, null, sel);
24288         
24289         
24290     }
24291     
24292     
24293     
24294     
24295     
24296 });
24297
24298
24299
24300
24301
24302 /*
24303  * Based on:
24304  * Ext JS Library 1.1.1
24305  * Copyright(c) 2006-2007, Ext JS, LLC.
24306  *
24307  * Originally Released Under LGPL - original licence link has changed is not relivant.
24308  *
24309  * Fork - LGPL
24310  * <script type="text/javascript">
24311  */
24312  
24313 /**
24314  * @class Roo.form.BasicForm
24315  * @extends Roo.util.Observable
24316  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
24317  * @constructor
24318  * @param {String/HTMLElement/Roo.Element} el The form element or its id
24319  * @param {Object} config Configuration options
24320  */
24321 Roo.form.BasicForm = function(el, config){
24322     this.allItems = [];
24323     this.childForms = [];
24324     Roo.apply(this, config);
24325     /*
24326      * The Roo.form.Field items in this form.
24327      * @type MixedCollection
24328      */
24329      
24330      
24331     this.items = new Roo.util.MixedCollection(false, function(o){
24332         return o.id || (o.id = Roo.id());
24333     });
24334     this.addEvents({
24335         /**
24336          * @event beforeaction
24337          * Fires before any action is performed. Return false to cancel the action.
24338          * @param {Form} this
24339          * @param {Action} action The action to be performed
24340          */
24341         beforeaction: true,
24342         /**
24343          * @event actionfailed
24344          * Fires when an action fails.
24345          * @param {Form} this
24346          * @param {Action} action The action that failed
24347          */
24348         actionfailed : true,
24349         /**
24350          * @event actioncomplete
24351          * Fires when an action is completed.
24352          * @param {Form} this
24353          * @param {Action} action The action that completed
24354          */
24355         actioncomplete : true
24356     });
24357     if(el){
24358         this.initEl(el);
24359     }
24360     Roo.form.BasicForm.superclass.constructor.call(this);
24361 };
24362
24363 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
24364     /**
24365      * @cfg {String} method
24366      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
24367      */
24368     /**
24369      * @cfg {DataReader} reader
24370      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
24371      * This is optional as there is built-in support for processing JSON.
24372      */
24373     /**
24374      * @cfg {DataReader} errorReader
24375      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
24376      * This is completely optional as there is built-in support for processing JSON.
24377      */
24378     /**
24379      * @cfg {String} url
24380      * The URL to use for form actions if one isn't supplied in the action options.
24381      */
24382     /**
24383      * @cfg {Boolean} fileUpload
24384      * Set to true if this form is a file upload.
24385      */
24386      
24387     /**
24388      * @cfg {Object} baseParams
24389      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
24390      */
24391      /**
24392      
24393     /**
24394      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
24395      */
24396     timeout: 30,
24397
24398     // private
24399     activeAction : null,
24400
24401     /**
24402      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
24403      * or setValues() data instead of when the form was first created.
24404      */
24405     trackResetOnLoad : false,
24406     
24407     
24408     /**
24409      * childForms - used for multi-tab forms
24410      * @type {Array}
24411      */
24412     childForms : false,
24413     
24414     /**
24415      * allItems - full list of fields.
24416      * @type {Array}
24417      */
24418     allItems : false,
24419     
24420     /**
24421      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
24422      * element by passing it or its id or mask the form itself by passing in true.
24423      * @type Mixed
24424      */
24425     waitMsgTarget : false,
24426     
24427     /**
24428      * @type Boolean
24429      */
24430     disableMask : false,
24431
24432     // private
24433     initEl : function(el){
24434         this.el = Roo.get(el);
24435         this.id = this.el.id || Roo.id();
24436         this.el.on('submit', this.onSubmit, this);
24437         this.el.addClass('x-form');
24438     },
24439
24440     // private
24441     onSubmit : function(e){
24442         e.stopEvent();
24443     },
24444
24445     /**
24446      * Returns true if client-side validation on the form is successful.
24447      * @return Boolean
24448      */
24449     isValid : function(){
24450         var valid = true;
24451         this.items.each(function(f){
24452            if(!f.validate()){
24453                valid = false;
24454            }
24455         });
24456         return valid;
24457     },
24458
24459     /**
24460      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
24461      * @return Boolean
24462      */
24463     isDirty : function(){
24464         var dirty = false;
24465         this.items.each(function(f){
24466            if(f.isDirty()){
24467                dirty = true;
24468                return false;
24469            }
24470         });
24471         return dirty;
24472     },
24473     
24474     /**
24475      * Returns true if any fields in this form have changed since their original load. (New version)
24476      * @return Boolean
24477      */
24478     
24479     hasChanged : function()
24480     {
24481         var dirty = false;
24482         this.items.each(function(f){
24483            if(f.hasChanged()){
24484                dirty = true;
24485                return false;
24486            }
24487         });
24488         return dirty;
24489         
24490     },
24491     /**
24492      * Resets all hasChanged to 'false' -
24493      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
24494      * So hasChanged storage is only to be used for this purpose
24495      * @return Boolean
24496      */
24497     resetHasChanged : function()
24498     {
24499         this.items.each(function(f){
24500            f.resetHasChanged();
24501         });
24502         
24503     },
24504     
24505     
24506     /**
24507      * Performs a predefined action (submit or load) or custom actions you define on this form.
24508      * @param {String} actionName The name of the action type
24509      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
24510      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
24511      * accept other config options):
24512      * <pre>
24513 Property          Type             Description
24514 ----------------  ---------------  ----------------------------------------------------------------------------------
24515 url               String           The url for the action (defaults to the form's url)
24516 method            String           The form method to use (defaults to the form's method, or POST if not defined)
24517 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
24518 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
24519                                    validate the form on the client (defaults to false)
24520      * </pre>
24521      * @return {BasicForm} this
24522      */
24523     doAction : function(action, options){
24524         if(typeof action == 'string'){
24525             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
24526         }
24527         if(this.fireEvent('beforeaction', this, action) !== false){
24528             this.beforeAction(action);
24529             action.run.defer(100, action);
24530         }
24531         return this;
24532     },
24533
24534     /**
24535      * Shortcut to do a submit action.
24536      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
24537      * @return {BasicForm} this
24538      */
24539     submit : function(options){
24540         this.doAction('submit', options);
24541         return this;
24542     },
24543
24544     /**
24545      * Shortcut to do a load action.
24546      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
24547      * @return {BasicForm} this
24548      */
24549     load : function(options){
24550         this.doAction('load', options);
24551         return this;
24552     },
24553
24554     /**
24555      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
24556      * @param {Record} record The record to edit
24557      * @return {BasicForm} this
24558      */
24559     updateRecord : function(record){
24560         record.beginEdit();
24561         var fs = record.fields;
24562         fs.each(function(f){
24563             var field = this.findField(f.name);
24564             if(field){
24565                 record.set(f.name, field.getValue());
24566             }
24567         }, this);
24568         record.endEdit();
24569         return this;
24570     },
24571
24572     /**
24573      * Loads an Roo.data.Record into this form.
24574      * @param {Record} record The record to load
24575      * @return {BasicForm} this
24576      */
24577     loadRecord : function(record){
24578         this.setValues(record.data);
24579         return this;
24580     },
24581
24582     // private
24583     beforeAction : function(action){
24584         var o = action.options;
24585         
24586         if(!this.disableMask) {
24587             if(this.waitMsgTarget === true){
24588                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
24589             }else if(this.waitMsgTarget){
24590                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
24591                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
24592             }else {
24593                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
24594             }
24595         }
24596         
24597          
24598     },
24599
24600     // private
24601     afterAction : function(action, success){
24602         this.activeAction = null;
24603         var o = action.options;
24604         
24605         if(!this.disableMask) {
24606             if(this.waitMsgTarget === true){
24607                 this.el.unmask();
24608             }else if(this.waitMsgTarget){
24609                 this.waitMsgTarget.unmask();
24610             }else{
24611                 Roo.MessageBox.updateProgress(1);
24612                 Roo.MessageBox.hide();
24613             }
24614         }
24615         
24616         if(success){
24617             if(o.reset){
24618                 this.reset();
24619             }
24620             Roo.callback(o.success, o.scope, [this, action]);
24621             this.fireEvent('actioncomplete', this, action);
24622             
24623         }else{
24624             
24625             // failure condition..
24626             // we have a scenario where updates need confirming.
24627             // eg. if a locking scenario exists..
24628             // we look for { errors : { needs_confirm : true }} in the response.
24629             if (
24630                 (typeof(action.result) != 'undefined')  &&
24631                 (typeof(action.result.errors) != 'undefined')  &&
24632                 (typeof(action.result.errors.needs_confirm) != 'undefined')
24633            ){
24634                 var _t = this;
24635                 Roo.MessageBox.confirm(
24636                     "Change requires confirmation",
24637                     action.result.errorMsg,
24638                     function(r) {
24639                         if (r != 'yes') {
24640                             return;
24641                         }
24642                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
24643                     }
24644                     
24645                 );
24646                 
24647                 
24648                 
24649                 return;
24650             }
24651             
24652             Roo.callback(o.failure, o.scope, [this, action]);
24653             // show an error message if no failed handler is set..
24654             if (!this.hasListener('actionfailed')) {
24655                 Roo.MessageBox.alert("Error",
24656                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
24657                         action.result.errorMsg :
24658                         "Saving Failed, please check your entries or try again"
24659                 );
24660             }
24661             
24662             this.fireEvent('actionfailed', this, action);
24663         }
24664         
24665     },
24666
24667     /**
24668      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
24669      * @param {String} id The value to search for
24670      * @return Field
24671      */
24672     findField : function(id){
24673         var field = this.items.get(id);
24674         if(!field){
24675             this.items.each(function(f){
24676                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
24677                     field = f;
24678                     return false;
24679                 }
24680             });
24681         }
24682         return field || null;
24683     },
24684
24685     /**
24686      * Add a secondary form to this one, 
24687      * Used to provide tabbed forms. One form is primary, with hidden values 
24688      * which mirror the elements from the other forms.
24689      * 
24690      * @param {Roo.form.Form} form to add.
24691      * 
24692      */
24693     addForm : function(form)
24694     {
24695        
24696         if (this.childForms.indexOf(form) > -1) {
24697             // already added..
24698             return;
24699         }
24700         this.childForms.push(form);
24701         var n = '';
24702         Roo.each(form.allItems, function (fe) {
24703             
24704             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
24705             if (this.findField(n)) { // already added..
24706                 return;
24707             }
24708             var add = new Roo.form.Hidden({
24709                 name : n
24710             });
24711             add.render(this.el);
24712             
24713             this.add( add );
24714         }, this);
24715         
24716     },
24717     /**
24718      * Mark fields in this form invalid in bulk.
24719      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
24720      * @return {BasicForm} this
24721      */
24722     markInvalid : function(errors){
24723         if(errors instanceof Array){
24724             for(var i = 0, len = errors.length; i < len; i++){
24725                 var fieldError = errors[i];
24726                 var f = this.findField(fieldError.id);
24727                 if(f){
24728                     f.markInvalid(fieldError.msg);
24729                 }
24730             }
24731         }else{
24732             var field, id;
24733             for(id in errors){
24734                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
24735                     field.markInvalid(errors[id]);
24736                 }
24737             }
24738         }
24739         Roo.each(this.childForms || [], function (f) {
24740             f.markInvalid(errors);
24741         });
24742         
24743         return this;
24744     },
24745
24746     /**
24747      * Set values for fields in this form in bulk.
24748      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
24749      * @return {BasicForm} this
24750      */
24751     setValues : function(values){
24752         if(values instanceof Array){ // array of objects
24753             for(var i = 0, len = values.length; i < len; i++){
24754                 var v = values[i];
24755                 var f = this.findField(v.id);
24756                 if(f){
24757                     f.setValue(v.value);
24758                     if(this.trackResetOnLoad){
24759                         f.originalValue = f.getValue();
24760                     }
24761                 }
24762             }
24763         }else{ // object hash
24764             var field, id;
24765             for(id in values){
24766                 if(typeof values[id] != 'function' && (field = this.findField(id))){
24767                     
24768                     if (field.setFromData && 
24769                         field.valueField && 
24770                         field.displayField &&
24771                         // combos' with local stores can 
24772                         // be queried via setValue()
24773                         // to set their value..
24774                         (field.store && !field.store.isLocal)
24775                         ) {
24776                         // it's a combo
24777                         var sd = { };
24778                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
24779                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
24780                         field.setFromData(sd);
24781                         
24782                     } else {
24783                         field.setValue(values[id]);
24784                     }
24785                     
24786                     
24787                     if(this.trackResetOnLoad){
24788                         field.originalValue = field.getValue();
24789                     }
24790                 }
24791             }
24792         }
24793         this.resetHasChanged();
24794         
24795         
24796         Roo.each(this.childForms || [], function (f) {
24797             f.setValues(values);
24798             f.resetHasChanged();
24799         });
24800                 
24801         return this;
24802     },
24803
24804     /**
24805      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
24806      * they are returned as an array.
24807      * @param {Boolean} asString
24808      * @return {Object}
24809      */
24810     getValues : function(asString){
24811         if (this.childForms) {
24812             // copy values from the child forms
24813             Roo.each(this.childForms, function (f) {
24814                 this.setValues(f.getValues());
24815             }, this);
24816         }
24817         
24818         
24819         
24820         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
24821         if(asString === true){
24822             return fs;
24823         }
24824         return Roo.urlDecode(fs);
24825     },
24826     
24827     /**
24828      * Returns the fields in this form as an object with key/value pairs. 
24829      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
24830      * @return {Object}
24831      */
24832     getFieldValues : function(with_hidden)
24833     {
24834         if (this.childForms) {
24835             // copy values from the child forms
24836             // should this call getFieldValues - probably not as we do not currently copy
24837             // hidden fields when we generate..
24838             Roo.each(this.childForms, function (f) {
24839                 this.setValues(f.getValues());
24840             }, this);
24841         }
24842         
24843         var ret = {};
24844         this.items.each(function(f){
24845             if (!f.getName()) {
24846                 return;
24847             }
24848             var v = f.getValue();
24849             if (f.inputType =='radio') {
24850                 if (typeof(ret[f.getName()]) == 'undefined') {
24851                     ret[f.getName()] = ''; // empty..
24852                 }
24853                 
24854                 if (!f.el.dom.checked) {
24855                     return;
24856                     
24857                 }
24858                 v = f.el.dom.value;
24859                 
24860             }
24861             
24862             // not sure if this supported any more..
24863             if ((typeof(v) == 'object') && f.getRawValue) {
24864                 v = f.getRawValue() ; // dates..
24865             }
24866             // combo boxes where name != hiddenName...
24867             if (f.name != f.getName()) {
24868                 ret[f.name] = f.getRawValue();
24869             }
24870             ret[f.getName()] = v;
24871         });
24872         
24873         return ret;
24874     },
24875
24876     /**
24877      * Clears all invalid messages in this form.
24878      * @return {BasicForm} this
24879      */
24880     clearInvalid : function(){
24881         this.items.each(function(f){
24882            f.clearInvalid();
24883         });
24884         
24885         Roo.each(this.childForms || [], function (f) {
24886             f.clearInvalid();
24887         });
24888         
24889         
24890         return this;
24891     },
24892
24893     /**
24894      * Resets this form.
24895      * @return {BasicForm} this
24896      */
24897     reset : function(){
24898         this.items.each(function(f){
24899             f.reset();
24900         });
24901         
24902         Roo.each(this.childForms || [], function (f) {
24903             f.reset();
24904         });
24905         this.resetHasChanged();
24906         
24907         return this;
24908     },
24909
24910     /**
24911      * Add Roo.form components to this form.
24912      * @param {Field} field1
24913      * @param {Field} field2 (optional)
24914      * @param {Field} etc (optional)
24915      * @return {BasicForm} this
24916      */
24917     add : function(){
24918         this.items.addAll(Array.prototype.slice.call(arguments, 0));
24919         return this;
24920     },
24921
24922
24923     /**
24924      * Removes a field from the items collection (does NOT remove its markup).
24925      * @param {Field} field
24926      * @return {BasicForm} this
24927      */
24928     remove : function(field){
24929         this.items.remove(field);
24930         return this;
24931     },
24932
24933     /**
24934      * Looks at the fields in this form, checks them for an id attribute,
24935      * and calls applyTo on the existing dom element with that id.
24936      * @return {BasicForm} this
24937      */
24938     render : function(){
24939         this.items.each(function(f){
24940             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
24941                 f.applyTo(f.id);
24942             }
24943         });
24944         return this;
24945     },
24946
24947     /**
24948      * Calls {@link Ext#apply} for all fields in this form with the passed object.
24949      * @param {Object} values
24950      * @return {BasicForm} this
24951      */
24952     applyToFields : function(o){
24953         this.items.each(function(f){
24954            Roo.apply(f, o);
24955         });
24956         return this;
24957     },
24958
24959     /**
24960      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
24961      * @param {Object} values
24962      * @return {BasicForm} this
24963      */
24964     applyIfToFields : function(o){
24965         this.items.each(function(f){
24966            Roo.applyIf(f, o);
24967         });
24968         return this;
24969     }
24970 });
24971
24972 // back compat
24973 Roo.BasicForm = Roo.form.BasicForm;/*
24974  * Based on:
24975  * Ext JS Library 1.1.1
24976  * Copyright(c) 2006-2007, Ext JS, LLC.
24977  *
24978  * Originally Released Under LGPL - original licence link has changed is not relivant.
24979  *
24980  * Fork - LGPL
24981  * <script type="text/javascript">
24982  */
24983
24984 /**
24985  * @class Roo.form.Form
24986  * @extends Roo.form.BasicForm
24987  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
24988  * @constructor
24989  * @param {Object} config Configuration options
24990  */
24991 Roo.form.Form = function(config){
24992     var xitems =  [];
24993     if (config.items) {
24994         xitems = config.items;
24995         delete config.items;
24996     }
24997    
24998     
24999     Roo.form.Form.superclass.constructor.call(this, null, config);
25000     this.url = this.url || this.action;
25001     if(!this.root){
25002         this.root = new Roo.form.Layout(Roo.applyIf({
25003             id: Roo.id()
25004         }, config));
25005     }
25006     this.active = this.root;
25007     /**
25008      * Array of all the buttons that have been added to this form via {@link addButton}
25009      * @type Array
25010      */
25011     this.buttons = [];
25012     this.allItems = [];
25013     this.addEvents({
25014         /**
25015          * @event clientvalidation
25016          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
25017          * @param {Form} this
25018          * @param {Boolean} valid true if the form has passed client-side validation
25019          */
25020         clientvalidation: true,
25021         /**
25022          * @event rendered
25023          * Fires when the form is rendered
25024          * @param {Roo.form.Form} form
25025          */
25026         rendered : true
25027     });
25028     
25029     if (this.progressUrl) {
25030             // push a hidden field onto the list of fields..
25031             this.addxtype( {
25032                     xns: Roo.form, 
25033                     xtype : 'Hidden', 
25034                     name : 'UPLOAD_IDENTIFIER' 
25035             });
25036         }
25037         
25038     
25039     Roo.each(xitems, this.addxtype, this);
25040     
25041     
25042     
25043 };
25044
25045 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
25046     /**
25047      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
25048      */
25049     /**
25050      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
25051      */
25052     /**
25053      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
25054      */
25055     buttonAlign:'center',
25056
25057     /**
25058      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
25059      */
25060     minButtonWidth:75,
25061
25062     /**
25063      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
25064      * This property cascades to child containers if not set.
25065      */
25066     labelAlign:'left',
25067
25068     /**
25069      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
25070      * fires a looping event with that state. This is required to bind buttons to the valid
25071      * state using the config value formBind:true on the button.
25072      */
25073     monitorValid : false,
25074
25075     /**
25076      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
25077      */
25078     monitorPoll : 200,
25079     
25080     /**
25081      * @cfg {String} progressUrl - Url to return progress data 
25082      */
25083     
25084     progressUrl : false,
25085     /**
25086      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
25087      * sending a formdata with extra parameters - eg uploaded elements.
25088      */
25089     
25090     formData : false,
25091     
25092     /**
25093      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
25094      * fields are added and the column is closed. If no fields are passed the column remains open
25095      * until end() is called.
25096      * @param {Object} config The config to pass to the column
25097      * @param {Field} field1 (optional)
25098      * @param {Field} field2 (optional)
25099      * @param {Field} etc (optional)
25100      * @return Column The column container object
25101      */
25102     column : function(c){
25103         var col = new Roo.form.Column(c);
25104         this.start(col);
25105         if(arguments.length > 1){ // duplicate code required because of Opera
25106             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25107             this.end();
25108         }
25109         return col;
25110     },
25111
25112     /**
25113      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
25114      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
25115      * until end() is called.
25116      * @param {Object} config The config to pass to the fieldset
25117      * @param {Field} field1 (optional)
25118      * @param {Field} field2 (optional)
25119      * @param {Field} etc (optional)
25120      * @return FieldSet The fieldset container object
25121      */
25122     fieldset : function(c){
25123         var fs = new Roo.form.FieldSet(c);
25124         this.start(fs);
25125         if(arguments.length > 1){ // duplicate code required because of Opera
25126             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25127             this.end();
25128         }
25129         return fs;
25130     },
25131
25132     /**
25133      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
25134      * fields are added and the container is closed. If no fields are passed the container remains open
25135      * until end() is called.
25136      * @param {Object} config The config to pass to the Layout
25137      * @param {Field} field1 (optional)
25138      * @param {Field} field2 (optional)
25139      * @param {Field} etc (optional)
25140      * @return Layout The container object
25141      */
25142     container : function(c){
25143         var l = new Roo.form.Layout(c);
25144         this.start(l);
25145         if(arguments.length > 1){ // duplicate code required because of Opera
25146             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25147             this.end();
25148         }
25149         return l;
25150     },
25151
25152     /**
25153      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
25154      * @param {Object} container A Roo.form.Layout or subclass of Layout
25155      * @return {Form} this
25156      */
25157     start : function(c){
25158         // cascade label info
25159         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
25160         this.active.stack.push(c);
25161         c.ownerCt = this.active;
25162         this.active = c;
25163         return this;
25164     },
25165
25166     /**
25167      * Closes the current open container
25168      * @return {Form} this
25169      */
25170     end : function(){
25171         if(this.active == this.root){
25172             return this;
25173         }
25174         this.active = this.active.ownerCt;
25175         return this;
25176     },
25177
25178     /**
25179      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
25180      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
25181      * as the label of the field.
25182      * @param {Field} field1
25183      * @param {Field} field2 (optional)
25184      * @param {Field} etc. (optional)
25185      * @return {Form} this
25186      */
25187     add : function(){
25188         this.active.stack.push.apply(this.active.stack, arguments);
25189         this.allItems.push.apply(this.allItems,arguments);
25190         var r = [];
25191         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
25192             if(a[i].isFormField){
25193                 r.push(a[i]);
25194             }
25195         }
25196         if(r.length > 0){
25197             Roo.form.Form.superclass.add.apply(this, r);
25198         }
25199         return this;
25200     },
25201     
25202
25203     
25204     
25205     
25206      /**
25207      * Find any element that has been added to a form, using it's ID or name
25208      * This can include framesets, columns etc. along with regular fields..
25209      * @param {String} id - id or name to find.
25210      
25211      * @return {Element} e - or false if nothing found.
25212      */
25213     findbyId : function(id)
25214     {
25215         var ret = false;
25216         if (!id) {
25217             return ret;
25218         }
25219         Roo.each(this.allItems, function(f){
25220             if (f.id == id || f.name == id ){
25221                 ret = f;
25222                 return false;
25223             }
25224         });
25225         return ret;
25226     },
25227
25228     
25229     
25230     /**
25231      * Render this form into the passed container. This should only be called once!
25232      * @param {String/HTMLElement/Element} container The element this component should be rendered into
25233      * @return {Form} this
25234      */
25235     render : function(ct)
25236     {
25237         
25238         
25239         
25240         ct = Roo.get(ct);
25241         var o = this.autoCreate || {
25242             tag: 'form',
25243             method : this.method || 'POST',
25244             id : this.id || Roo.id()
25245         };
25246         this.initEl(ct.createChild(o));
25247
25248         this.root.render(this.el);
25249         
25250        
25251              
25252         this.items.each(function(f){
25253             f.render('x-form-el-'+f.id);
25254         });
25255
25256         if(this.buttons.length > 0){
25257             // tables are required to maintain order and for correct IE layout
25258             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
25259                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
25260                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
25261             }}, null, true);
25262             var tr = tb.getElementsByTagName('tr')[0];
25263             for(var i = 0, len = this.buttons.length; i < len; i++) {
25264                 var b = this.buttons[i];
25265                 var td = document.createElement('td');
25266                 td.className = 'x-form-btn-td';
25267                 b.render(tr.appendChild(td));
25268             }
25269         }
25270         if(this.monitorValid){ // initialize after render
25271             this.startMonitoring();
25272         }
25273         this.fireEvent('rendered', this);
25274         return this;
25275     },
25276
25277     /**
25278      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
25279      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
25280      * object or a valid Roo.DomHelper element config
25281      * @param {Function} handler The function called when the button is clicked
25282      * @param {Object} scope (optional) The scope of the handler function
25283      * @return {Roo.Button}
25284      */
25285     addButton : function(config, handler, scope){
25286         var bc = {
25287             handler: handler,
25288             scope: scope,
25289             minWidth: this.minButtonWidth,
25290             hideParent:true
25291         };
25292         if(typeof config == "string"){
25293             bc.text = config;
25294         }else{
25295             Roo.apply(bc, config);
25296         }
25297         var btn = new Roo.Button(null, bc);
25298         this.buttons.push(btn);
25299         return btn;
25300     },
25301
25302      /**
25303      * Adds a series of form elements (using the xtype property as the factory method.
25304      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
25305      * @param {Object} config 
25306      */
25307     
25308     addxtype : function()
25309     {
25310         var ar = Array.prototype.slice.call(arguments, 0);
25311         var ret = false;
25312         for(var i = 0; i < ar.length; i++) {
25313             if (!ar[i]) {
25314                 continue; // skip -- if this happends something invalid got sent, we 
25315                 // should ignore it, as basically that interface element will not show up
25316                 // and that should be pretty obvious!!
25317             }
25318             
25319             if (Roo.form[ar[i].xtype]) {
25320                 ar[i].form = this;
25321                 var fe = Roo.factory(ar[i], Roo.form);
25322                 if (!ret) {
25323                     ret = fe;
25324                 }
25325                 fe.form = this;
25326                 if (fe.store) {
25327                     fe.store.form = this;
25328                 }
25329                 if (fe.isLayout) {  
25330                          
25331                     this.start(fe);
25332                     this.allItems.push(fe);
25333                     if (fe.items && fe.addxtype) {
25334                         fe.addxtype.apply(fe, fe.items);
25335                         delete fe.items;
25336                     }
25337                      this.end();
25338                     continue;
25339                 }
25340                 
25341                 
25342                  
25343                 this.add(fe);
25344               //  console.log('adding ' + ar[i].xtype);
25345             }
25346             if (ar[i].xtype == 'Button') {  
25347                 //console.log('adding button');
25348                 //console.log(ar[i]);
25349                 this.addButton(ar[i]);
25350                 this.allItems.push(fe);
25351                 continue;
25352             }
25353             
25354             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
25355                 alert('end is not supported on xtype any more, use items');
25356             //    this.end();
25357             //    //console.log('adding end');
25358             }
25359             
25360         }
25361         return ret;
25362     },
25363     
25364     /**
25365      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
25366      * option "monitorValid"
25367      */
25368     startMonitoring : function(){
25369         if(!this.bound){
25370             this.bound = true;
25371             Roo.TaskMgr.start({
25372                 run : this.bindHandler,
25373                 interval : this.monitorPoll || 200,
25374                 scope: this
25375             });
25376         }
25377     },
25378
25379     /**
25380      * Stops monitoring of the valid state of this form
25381      */
25382     stopMonitoring : function(){
25383         this.bound = false;
25384     },
25385
25386     // private
25387     bindHandler : function(){
25388         if(!this.bound){
25389             return false; // stops binding
25390         }
25391         var valid = true;
25392         this.items.each(function(f){
25393             if(!f.isValid(true)){
25394                 valid = false;
25395                 return false;
25396             }
25397         });
25398         for(var i = 0, len = this.buttons.length; i < len; i++){
25399             var btn = this.buttons[i];
25400             if(btn.formBind === true && btn.disabled === valid){
25401                 btn.setDisabled(!valid);
25402             }
25403         }
25404         this.fireEvent('clientvalidation', this, valid);
25405     }
25406     
25407     
25408     
25409     
25410     
25411     
25412     
25413     
25414 });
25415
25416
25417 // back compat
25418 Roo.Form = Roo.form.Form;
25419 /*
25420  * Based on:
25421  * Ext JS Library 1.1.1
25422  * Copyright(c) 2006-2007, Ext JS, LLC.
25423  *
25424  * Originally Released Under LGPL - original licence link has changed is not relivant.
25425  *
25426  * Fork - LGPL
25427  * <script type="text/javascript">
25428  */
25429
25430 // as we use this in bootstrap.
25431 Roo.namespace('Roo.form');
25432  /**
25433  * @class Roo.form.Action
25434  * Internal Class used to handle form actions
25435  * @constructor
25436  * @param {Roo.form.BasicForm} el The form element or its id
25437  * @param {Object} config Configuration options
25438  */
25439
25440  
25441  
25442 // define the action interface
25443 Roo.form.Action = function(form, options){
25444     this.form = form;
25445     this.options = options || {};
25446 };
25447 /**
25448  * Client Validation Failed
25449  * @const 
25450  */
25451 Roo.form.Action.CLIENT_INVALID = 'client';
25452 /**
25453  * Server Validation Failed
25454  * @const 
25455  */
25456 Roo.form.Action.SERVER_INVALID = 'server';
25457  /**
25458  * Connect to Server Failed
25459  * @const 
25460  */
25461 Roo.form.Action.CONNECT_FAILURE = 'connect';
25462 /**
25463  * Reading Data from Server Failed
25464  * @const 
25465  */
25466 Roo.form.Action.LOAD_FAILURE = 'load';
25467
25468 Roo.form.Action.prototype = {
25469     type : 'default',
25470     failureType : undefined,
25471     response : undefined,
25472     result : undefined,
25473
25474     // interface method
25475     run : function(options){
25476
25477     },
25478
25479     // interface method
25480     success : function(response){
25481
25482     },
25483
25484     // interface method
25485     handleResponse : function(response){
25486
25487     },
25488
25489     // default connection failure
25490     failure : function(response){
25491         
25492         this.response = response;
25493         this.failureType = Roo.form.Action.CONNECT_FAILURE;
25494         this.form.afterAction(this, false);
25495     },
25496
25497     processResponse : function(response){
25498         this.response = response;
25499         if(!response.responseText){
25500             return true;
25501         }
25502         this.result = this.handleResponse(response);
25503         return this.result;
25504     },
25505
25506     // utility functions used internally
25507     getUrl : function(appendParams){
25508         var url = this.options.url || this.form.url || this.form.el.dom.action;
25509         if(appendParams){
25510             var p = this.getParams();
25511             if(p){
25512                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
25513             }
25514         }
25515         return url;
25516     },
25517
25518     getMethod : function(){
25519         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
25520     },
25521
25522     getParams : function(){
25523         var bp = this.form.baseParams;
25524         var p = this.options.params;
25525         if(p){
25526             if(typeof p == "object"){
25527                 p = Roo.urlEncode(Roo.applyIf(p, bp));
25528             }else if(typeof p == 'string' && bp){
25529                 p += '&' + Roo.urlEncode(bp);
25530             }
25531         }else if(bp){
25532             p = Roo.urlEncode(bp);
25533         }
25534         return p;
25535     },
25536
25537     createCallback : function(){
25538         return {
25539             success: this.success,
25540             failure: this.failure,
25541             scope: this,
25542             timeout: (this.form.timeout*1000),
25543             upload: this.form.fileUpload ? this.success : undefined
25544         };
25545     }
25546 };
25547
25548 Roo.form.Action.Submit = function(form, options){
25549     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
25550 };
25551
25552 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
25553     type : 'submit',
25554
25555     haveProgress : false,
25556     uploadComplete : false,
25557     
25558     // uploadProgress indicator.
25559     uploadProgress : function()
25560     {
25561         if (!this.form.progressUrl) {
25562             return;
25563         }
25564         
25565         if (!this.haveProgress) {
25566             Roo.MessageBox.progress("Uploading", "Uploading");
25567         }
25568         if (this.uploadComplete) {
25569            Roo.MessageBox.hide();
25570            return;
25571         }
25572         
25573         this.haveProgress = true;
25574    
25575         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
25576         
25577         var c = new Roo.data.Connection();
25578         c.request({
25579             url : this.form.progressUrl,
25580             params: {
25581                 id : uid
25582             },
25583             method: 'GET',
25584             success : function(req){
25585                //console.log(data);
25586                 var rdata = false;
25587                 var edata;
25588                 try  {
25589                    rdata = Roo.decode(req.responseText)
25590                 } catch (e) {
25591                     Roo.log("Invalid data from server..");
25592                     Roo.log(edata);
25593                     return;
25594                 }
25595                 if (!rdata || !rdata.success) {
25596                     Roo.log(rdata);
25597                     Roo.MessageBox.alert(Roo.encode(rdata));
25598                     return;
25599                 }
25600                 var data = rdata.data;
25601                 
25602                 if (this.uploadComplete) {
25603                    Roo.MessageBox.hide();
25604                    return;
25605                 }
25606                    
25607                 if (data){
25608                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
25609                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
25610                     );
25611                 }
25612                 this.uploadProgress.defer(2000,this);
25613             },
25614        
25615             failure: function(data) {
25616                 Roo.log('progress url failed ');
25617                 Roo.log(data);
25618             },
25619             scope : this
25620         });
25621            
25622     },
25623     
25624     
25625     run : function()
25626     {
25627         // run get Values on the form, so it syncs any secondary forms.
25628         this.form.getValues();
25629         
25630         var o = this.options;
25631         var method = this.getMethod();
25632         var isPost = method == 'POST';
25633         if(o.clientValidation === false || this.form.isValid()){
25634             
25635             if (this.form.progressUrl) {
25636                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
25637                     (new Date() * 1) + '' + Math.random());
25638                     
25639             } 
25640             
25641             
25642             Roo.Ajax.request(Roo.apply(this.createCallback(), {
25643                 form:this.form.el.dom,
25644                 url:this.getUrl(!isPost),
25645                 method: method,
25646                 params:isPost ? this.getParams() : null,
25647                 isUpload: this.form.fileUpload,
25648                 formData : this.form.formData
25649             }));
25650             
25651             this.uploadProgress();
25652
25653         }else if (o.clientValidation !== false){ // client validation failed
25654             this.failureType = Roo.form.Action.CLIENT_INVALID;
25655             this.form.afterAction(this, false);
25656         }
25657     },
25658
25659     success : function(response)
25660     {
25661         this.uploadComplete= true;
25662         if (this.haveProgress) {
25663             Roo.MessageBox.hide();
25664         }
25665         
25666         
25667         var result = this.processResponse(response);
25668         if(result === true || result.success){
25669             this.form.afterAction(this, true);
25670             return;
25671         }
25672         if(result.errors){
25673             this.form.markInvalid(result.errors);
25674             this.failureType = Roo.form.Action.SERVER_INVALID;
25675         }
25676         this.form.afterAction(this, false);
25677     },
25678     failure : function(response)
25679     {
25680         this.uploadComplete= true;
25681         if (this.haveProgress) {
25682             Roo.MessageBox.hide();
25683         }
25684         
25685         this.response = response;
25686         this.failureType = Roo.form.Action.CONNECT_FAILURE;
25687         this.form.afterAction(this, false);
25688     },
25689     
25690     handleResponse : function(response){
25691         if(this.form.errorReader){
25692             var rs = this.form.errorReader.read(response);
25693             var errors = [];
25694             if(rs.records){
25695                 for(var i = 0, len = rs.records.length; i < len; i++) {
25696                     var r = rs.records[i];
25697                     errors[i] = r.data;
25698                 }
25699             }
25700             if(errors.length < 1){
25701                 errors = null;
25702             }
25703             return {
25704                 success : rs.success,
25705                 errors : errors
25706             };
25707         }
25708         var ret = false;
25709         try {
25710             ret = Roo.decode(response.responseText);
25711         } catch (e) {
25712             ret = {
25713                 success: false,
25714                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
25715                 errors : []
25716             };
25717         }
25718         return ret;
25719         
25720     }
25721 });
25722
25723
25724 Roo.form.Action.Load = function(form, options){
25725     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
25726     this.reader = this.form.reader;
25727 };
25728
25729 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
25730     type : 'load',
25731
25732     run : function(){
25733         
25734         Roo.Ajax.request(Roo.apply(
25735                 this.createCallback(), {
25736                     method:this.getMethod(),
25737                     url:this.getUrl(false),
25738                     params:this.getParams()
25739         }));
25740     },
25741
25742     success : function(response){
25743         
25744         var result = this.processResponse(response);
25745         if(result === true || !result.success || !result.data){
25746             this.failureType = Roo.form.Action.LOAD_FAILURE;
25747             this.form.afterAction(this, false);
25748             return;
25749         }
25750         this.form.clearInvalid();
25751         this.form.setValues(result.data);
25752         this.form.afterAction(this, true);
25753     },
25754
25755     handleResponse : function(response){
25756         if(this.form.reader){
25757             var rs = this.form.reader.read(response);
25758             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
25759             return {
25760                 success : rs.success,
25761                 data : data
25762             };
25763         }
25764         return Roo.decode(response.responseText);
25765     }
25766 });
25767
25768 Roo.form.Action.ACTION_TYPES = {
25769     'load' : Roo.form.Action.Load,
25770     'submit' : Roo.form.Action.Submit
25771 };/*
25772  * Based on:
25773  * Ext JS Library 1.1.1
25774  * Copyright(c) 2006-2007, Ext JS, LLC.
25775  *
25776  * Originally Released Under LGPL - original licence link has changed is not relivant.
25777  *
25778  * Fork - LGPL
25779  * <script type="text/javascript">
25780  */
25781  
25782 /**
25783  * @class Roo.form.Layout
25784  * @extends Roo.Component
25785  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
25786  * @constructor
25787  * @param {Object} config Configuration options
25788  */
25789 Roo.form.Layout = function(config){
25790     var xitems = [];
25791     if (config.items) {
25792         xitems = config.items;
25793         delete config.items;
25794     }
25795     Roo.form.Layout.superclass.constructor.call(this, config);
25796     this.stack = [];
25797     Roo.each(xitems, this.addxtype, this);
25798      
25799 };
25800
25801 Roo.extend(Roo.form.Layout, Roo.Component, {
25802     /**
25803      * @cfg {String/Object} autoCreate
25804      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
25805      */
25806     /**
25807      * @cfg {String/Object/Function} style
25808      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
25809      * a function which returns such a specification.
25810      */
25811     /**
25812      * @cfg {String} labelAlign
25813      * Valid values are "left," "top" and "right" (defaults to "left")
25814      */
25815     /**
25816      * @cfg {Number} labelWidth
25817      * Fixed width in pixels of all field labels (defaults to undefined)
25818      */
25819     /**
25820      * @cfg {Boolean} clear
25821      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
25822      */
25823     clear : true,
25824     /**
25825      * @cfg {String} labelSeparator
25826      * The separator to use after field labels (defaults to ':')
25827      */
25828     labelSeparator : ':',
25829     /**
25830      * @cfg {Boolean} hideLabels
25831      * True to suppress the display of field labels in this layout (defaults to false)
25832      */
25833     hideLabels : false,
25834
25835     // private
25836     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
25837     
25838     isLayout : true,
25839     
25840     // private
25841     onRender : function(ct, position){
25842         if(this.el){ // from markup
25843             this.el = Roo.get(this.el);
25844         }else {  // generate
25845             var cfg = this.getAutoCreate();
25846             this.el = ct.createChild(cfg, position);
25847         }
25848         if(this.style){
25849             this.el.applyStyles(this.style);
25850         }
25851         if(this.labelAlign){
25852             this.el.addClass('x-form-label-'+this.labelAlign);
25853         }
25854         if(this.hideLabels){
25855             this.labelStyle = "display:none";
25856             this.elementStyle = "padding-left:0;";
25857         }else{
25858             if(typeof this.labelWidth == 'number'){
25859                 this.labelStyle = "width:"+this.labelWidth+"px;";
25860                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
25861             }
25862             if(this.labelAlign == 'top'){
25863                 this.labelStyle = "width:auto;";
25864                 this.elementStyle = "padding-left:0;";
25865             }
25866         }
25867         var stack = this.stack;
25868         var slen = stack.length;
25869         if(slen > 0){
25870             if(!this.fieldTpl){
25871                 var t = new Roo.Template(
25872                     '<div class="x-form-item {5}">',
25873                         '<label for="{0}" style="{2}">{1}{4}</label>',
25874                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
25875                         '</div>',
25876                     '</div><div class="x-form-clear-left"></div>'
25877                 );
25878                 t.disableFormats = true;
25879                 t.compile();
25880                 Roo.form.Layout.prototype.fieldTpl = t;
25881             }
25882             for(var i = 0; i < slen; i++) {
25883                 if(stack[i].isFormField){
25884                     this.renderField(stack[i]);
25885                 }else{
25886                     this.renderComponent(stack[i]);
25887                 }
25888             }
25889         }
25890         if(this.clear){
25891             this.el.createChild({cls:'x-form-clear'});
25892         }
25893     },
25894
25895     // private
25896     renderField : function(f){
25897         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
25898                f.id, //0
25899                f.fieldLabel, //1
25900                f.labelStyle||this.labelStyle||'', //2
25901                this.elementStyle||'', //3
25902                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
25903                f.itemCls||this.itemCls||''  //5
25904        ], true).getPrevSibling());
25905     },
25906
25907     // private
25908     renderComponent : function(c){
25909         c.render(c.isLayout ? this.el : this.el.createChild());    
25910     },
25911     /**
25912      * Adds a object form elements (using the xtype property as the factory method.)
25913      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
25914      * @param {Object} config 
25915      */
25916     addxtype : function(o)
25917     {
25918         // create the lement.
25919         o.form = this.form;
25920         var fe = Roo.factory(o, Roo.form);
25921         this.form.allItems.push(fe);
25922         this.stack.push(fe);
25923         
25924         if (fe.isFormField) {
25925             this.form.items.add(fe);
25926         }
25927          
25928         return fe;
25929     }
25930 });
25931
25932 /**
25933  * @class Roo.form.Column
25934  * @extends Roo.form.Layout
25935  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
25936  * @constructor
25937  * @param {Object} config Configuration options
25938  */
25939 Roo.form.Column = function(config){
25940     Roo.form.Column.superclass.constructor.call(this, config);
25941 };
25942
25943 Roo.extend(Roo.form.Column, Roo.form.Layout, {
25944     /**
25945      * @cfg {Number/String} width
25946      * The fixed width of the column in pixels or CSS value (defaults to "auto")
25947      */
25948     /**
25949      * @cfg {String/Object} autoCreate
25950      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
25951      */
25952
25953     // private
25954     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
25955
25956     // private
25957     onRender : function(ct, position){
25958         Roo.form.Column.superclass.onRender.call(this, ct, position);
25959         if(this.width){
25960             this.el.setWidth(this.width);
25961         }
25962     }
25963 });
25964
25965
25966 /**
25967  * @class Roo.form.Row
25968  * @extends Roo.form.Layout
25969  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
25970  * @constructor
25971  * @param {Object} config Configuration options
25972  */
25973
25974  
25975 Roo.form.Row = function(config){
25976     Roo.form.Row.superclass.constructor.call(this, config);
25977 };
25978  
25979 Roo.extend(Roo.form.Row, Roo.form.Layout, {
25980       /**
25981      * @cfg {Number/String} width
25982      * The fixed width of the column in pixels or CSS value (defaults to "auto")
25983      */
25984     /**
25985      * @cfg {Number/String} height
25986      * The fixed height of the column in pixels or CSS value (defaults to "auto")
25987      */
25988     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
25989     
25990     padWidth : 20,
25991     // private
25992     onRender : function(ct, position){
25993         //console.log('row render');
25994         if(!this.rowTpl){
25995             var t = new Roo.Template(
25996                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
25997                     '<label for="{0}" style="{2}">{1}{4}</label>',
25998                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
25999                     '</div>',
26000                 '</div>'
26001             );
26002             t.disableFormats = true;
26003             t.compile();
26004             Roo.form.Layout.prototype.rowTpl = t;
26005         }
26006         this.fieldTpl = this.rowTpl;
26007         
26008         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
26009         var labelWidth = 100;
26010         
26011         if ((this.labelAlign != 'top')) {
26012             if (typeof this.labelWidth == 'number') {
26013                 labelWidth = this.labelWidth
26014             }
26015             this.padWidth =  20 + labelWidth;
26016             
26017         }
26018         
26019         Roo.form.Column.superclass.onRender.call(this, ct, position);
26020         if(this.width){
26021             this.el.setWidth(this.width);
26022         }
26023         if(this.height){
26024             this.el.setHeight(this.height);
26025         }
26026     },
26027     
26028     // private
26029     renderField : function(f){
26030         f.fieldEl = this.fieldTpl.append(this.el, [
26031                f.id, f.fieldLabel,
26032                f.labelStyle||this.labelStyle||'',
26033                this.elementStyle||'',
26034                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
26035                f.itemCls||this.itemCls||'',
26036                f.width ? f.width + this.padWidth : 160 + this.padWidth
26037        ],true);
26038     }
26039 });
26040  
26041
26042 /**
26043  * @class Roo.form.FieldSet
26044  * @extends Roo.form.Layout
26045  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
26046  * @constructor
26047  * @param {Object} config Configuration options
26048  */
26049 Roo.form.FieldSet = function(config){
26050     Roo.form.FieldSet.superclass.constructor.call(this, config);
26051 };
26052
26053 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
26054     /**
26055      * @cfg {String} legend
26056      * The text to display as the legend for the FieldSet (defaults to '')
26057      */
26058     /**
26059      * @cfg {String/Object} autoCreate
26060      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
26061      */
26062
26063     // private
26064     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
26065
26066     // private
26067     onRender : function(ct, position){
26068         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
26069         if(this.legend){
26070             this.setLegend(this.legend);
26071         }
26072     },
26073
26074     // private
26075     setLegend : function(text){
26076         if(this.rendered){
26077             this.el.child('legend').update(text);
26078         }
26079     }
26080 });/*
26081  * Based on:
26082  * Ext JS Library 1.1.1
26083  * Copyright(c) 2006-2007, Ext JS, LLC.
26084  *
26085  * Originally Released Under LGPL - original licence link has changed is not relivant.
26086  *
26087  * Fork - LGPL
26088  * <script type="text/javascript">
26089  */
26090 /**
26091  * @class Roo.form.VTypes
26092  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
26093  * @singleton
26094  */
26095 Roo.form.VTypes = function(){
26096     // closure these in so they are only created once.
26097     var alpha = /^[a-zA-Z_]+$/;
26098     var alphanum = /^[a-zA-Z0-9_]+$/;
26099     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
26100     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
26101
26102     // All these messages and functions are configurable
26103     return {
26104         /**
26105          * The function used to validate email addresses
26106          * @param {String} value The email address
26107          */
26108         'email' : function(v){
26109             return email.test(v);
26110         },
26111         /**
26112          * The error text to display when the email validation function returns false
26113          * @type String
26114          */
26115         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
26116         /**
26117          * The keystroke filter mask to be applied on email input
26118          * @type RegExp
26119          */
26120         'emailMask' : /[a-z0-9_\.\-@]/i,
26121
26122         /**
26123          * The function used to validate URLs
26124          * @param {String} value The URL
26125          */
26126         'url' : function(v){
26127             return url.test(v);
26128         },
26129         /**
26130          * The error text to display when the url validation function returns false
26131          * @type String
26132          */
26133         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
26134         
26135         /**
26136          * The function used to validate alpha values
26137          * @param {String} value The value
26138          */
26139         'alpha' : function(v){
26140             return alpha.test(v);
26141         },
26142         /**
26143          * The error text to display when the alpha validation function returns false
26144          * @type String
26145          */
26146         'alphaText' : 'This field should only contain letters and _',
26147         /**
26148          * The keystroke filter mask to be applied on alpha input
26149          * @type RegExp
26150          */
26151         'alphaMask' : /[a-z_]/i,
26152
26153         /**
26154          * The function used to validate alphanumeric values
26155          * @param {String} value The value
26156          */
26157         'alphanum' : function(v){
26158             return alphanum.test(v);
26159         },
26160         /**
26161          * The error text to display when the alphanumeric validation function returns false
26162          * @type String
26163          */
26164         'alphanumText' : 'This field should only contain letters, numbers and _',
26165         /**
26166          * The keystroke filter mask to be applied on alphanumeric input
26167          * @type RegExp
26168          */
26169         'alphanumMask' : /[a-z0-9_]/i
26170     };
26171 }();//<script type="text/javascript">
26172
26173 /**
26174  * @class Roo.form.FCKeditor
26175  * @extends Roo.form.TextArea
26176  * Wrapper around the FCKEditor http://www.fckeditor.net
26177  * @constructor
26178  * Creates a new FCKeditor
26179  * @param {Object} config Configuration options
26180  */
26181 Roo.form.FCKeditor = function(config){
26182     Roo.form.FCKeditor.superclass.constructor.call(this, config);
26183     this.addEvents({
26184          /**
26185          * @event editorinit
26186          * Fired when the editor is initialized - you can add extra handlers here..
26187          * @param {FCKeditor} this
26188          * @param {Object} the FCK object.
26189          */
26190         editorinit : true
26191     });
26192     
26193     
26194 };
26195 Roo.form.FCKeditor.editors = { };
26196 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
26197 {
26198     //defaultAutoCreate : {
26199     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
26200     //},
26201     // private
26202     /**
26203      * @cfg {Object} fck options - see fck manual for details.
26204      */
26205     fckconfig : false,
26206     
26207     /**
26208      * @cfg {Object} fck toolbar set (Basic or Default)
26209      */
26210     toolbarSet : 'Basic',
26211     /**
26212      * @cfg {Object} fck BasePath
26213      */ 
26214     basePath : '/fckeditor/',
26215     
26216     
26217     frame : false,
26218     
26219     value : '',
26220     
26221    
26222     onRender : function(ct, position)
26223     {
26224         if(!this.el){
26225             this.defaultAutoCreate = {
26226                 tag: "textarea",
26227                 style:"width:300px;height:60px;",
26228                 autocomplete: "new-password"
26229             };
26230         }
26231         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
26232         /*
26233         if(this.grow){
26234             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
26235             if(this.preventScrollbars){
26236                 this.el.setStyle("overflow", "hidden");
26237             }
26238             this.el.setHeight(this.growMin);
26239         }
26240         */
26241         //console.log('onrender' + this.getId() );
26242         Roo.form.FCKeditor.editors[this.getId()] = this;
26243          
26244
26245         this.replaceTextarea() ;
26246         
26247     },
26248     
26249     getEditor : function() {
26250         return this.fckEditor;
26251     },
26252     /**
26253      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
26254      * @param {Mixed} value The value to set
26255      */
26256     
26257     
26258     setValue : function(value)
26259     {
26260         //console.log('setValue: ' + value);
26261         
26262         if(typeof(value) == 'undefined') { // not sure why this is happending...
26263             return;
26264         }
26265         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26266         
26267         //if(!this.el || !this.getEditor()) {
26268         //    this.value = value;
26269             //this.setValue.defer(100,this,[value]);    
26270         //    return;
26271         //} 
26272         
26273         if(!this.getEditor()) {
26274             return;
26275         }
26276         
26277         this.getEditor().SetData(value);
26278         
26279         //
26280
26281     },
26282
26283     /**
26284      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
26285      * @return {Mixed} value The field value
26286      */
26287     getValue : function()
26288     {
26289         
26290         if (this.frame && this.frame.dom.style.display == 'none') {
26291             return Roo.form.FCKeditor.superclass.getValue.call(this);
26292         }
26293         
26294         if(!this.el || !this.getEditor()) {
26295            
26296            // this.getValue.defer(100,this); 
26297             return this.value;
26298         }
26299        
26300         
26301         var value=this.getEditor().GetData();
26302         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26303         return Roo.form.FCKeditor.superclass.getValue.call(this);
26304         
26305
26306     },
26307
26308     /**
26309      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
26310      * @return {Mixed} value The field value
26311      */
26312     getRawValue : function()
26313     {
26314         if (this.frame && this.frame.dom.style.display == 'none') {
26315             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26316         }
26317         
26318         if(!this.el || !this.getEditor()) {
26319             //this.getRawValue.defer(100,this); 
26320             return this.value;
26321             return;
26322         }
26323         
26324         
26325         
26326         var value=this.getEditor().GetData();
26327         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
26328         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26329          
26330     },
26331     
26332     setSize : function(w,h) {
26333         
26334         
26335         
26336         //if (this.frame && this.frame.dom.style.display == 'none') {
26337         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26338         //    return;
26339         //}
26340         //if(!this.el || !this.getEditor()) {
26341         //    this.setSize.defer(100,this, [w,h]); 
26342         //    return;
26343         //}
26344         
26345         
26346         
26347         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26348         
26349         this.frame.dom.setAttribute('width', w);
26350         this.frame.dom.setAttribute('height', h);
26351         this.frame.setSize(w,h);
26352         
26353     },
26354     
26355     toggleSourceEdit : function(value) {
26356         
26357       
26358          
26359         this.el.dom.style.display = value ? '' : 'none';
26360         this.frame.dom.style.display = value ?  'none' : '';
26361         
26362     },
26363     
26364     
26365     focus: function(tag)
26366     {
26367         if (this.frame.dom.style.display == 'none') {
26368             return Roo.form.FCKeditor.superclass.focus.call(this);
26369         }
26370         if(!this.el || !this.getEditor()) {
26371             this.focus.defer(100,this, [tag]); 
26372             return;
26373         }
26374         
26375         
26376         
26377         
26378         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
26379         this.getEditor().Focus();
26380         if (tgs.length) {
26381             if (!this.getEditor().Selection.GetSelection()) {
26382                 this.focus.defer(100,this, [tag]); 
26383                 return;
26384             }
26385             
26386             
26387             var r = this.getEditor().EditorDocument.createRange();
26388             r.setStart(tgs[0],0);
26389             r.setEnd(tgs[0],0);
26390             this.getEditor().Selection.GetSelection().removeAllRanges();
26391             this.getEditor().Selection.GetSelection().addRange(r);
26392             this.getEditor().Focus();
26393         }
26394         
26395     },
26396     
26397     
26398     
26399     replaceTextarea : function()
26400     {
26401         if ( document.getElementById( this.getId() + '___Frame' ) ) {
26402             return ;
26403         }
26404         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
26405         //{
26406             // We must check the elements firstly using the Id and then the name.
26407         var oTextarea = document.getElementById( this.getId() );
26408         
26409         var colElementsByName = document.getElementsByName( this.getId() ) ;
26410          
26411         oTextarea.style.display = 'none' ;
26412
26413         if ( oTextarea.tabIndex ) {            
26414             this.TabIndex = oTextarea.tabIndex ;
26415         }
26416         
26417         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
26418         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
26419         this.frame = Roo.get(this.getId() + '___Frame')
26420     },
26421     
26422     _getConfigHtml : function()
26423     {
26424         var sConfig = '' ;
26425
26426         for ( var o in this.fckconfig ) {
26427             sConfig += sConfig.length > 0  ? '&amp;' : '';
26428             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
26429         }
26430
26431         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
26432     },
26433     
26434     
26435     _getIFrameHtml : function()
26436     {
26437         var sFile = 'fckeditor.html' ;
26438         /* no idea what this is about..
26439         try
26440         {
26441             if ( (/fcksource=true/i).test( window.top.location.search ) )
26442                 sFile = 'fckeditor.original.html' ;
26443         }
26444         catch (e) { 
26445         */
26446
26447         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
26448         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
26449         
26450         
26451         var html = '<iframe id="' + this.getId() +
26452             '___Frame" src="' + sLink +
26453             '" width="' + this.width +
26454             '" height="' + this.height + '"' +
26455             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
26456             ' frameborder="0" scrolling="no"></iframe>' ;
26457
26458         return html ;
26459     },
26460     
26461     _insertHtmlBefore : function( html, element )
26462     {
26463         if ( element.insertAdjacentHTML )       {
26464             // IE
26465             element.insertAdjacentHTML( 'beforeBegin', html ) ;
26466         } else { // Gecko
26467             var oRange = document.createRange() ;
26468             oRange.setStartBefore( element ) ;
26469             var oFragment = oRange.createContextualFragment( html );
26470             element.parentNode.insertBefore( oFragment, element ) ;
26471         }
26472     }
26473     
26474     
26475   
26476     
26477     
26478     
26479     
26480
26481 });
26482
26483 //Roo.reg('fckeditor', Roo.form.FCKeditor);
26484
26485 function FCKeditor_OnComplete(editorInstance){
26486     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
26487     f.fckEditor = editorInstance;
26488     //console.log("loaded");
26489     f.fireEvent('editorinit', f, editorInstance);
26490
26491   
26492
26493  
26494
26495
26496
26497
26498
26499
26500
26501
26502
26503
26504
26505
26506
26507
26508
26509 //<script type="text/javascript">
26510 /**
26511  * @class Roo.form.GridField
26512  * @extends Roo.form.Field
26513  * Embed a grid (or editable grid into a form)
26514  * STATUS ALPHA
26515  * 
26516  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
26517  * it needs 
26518  * xgrid.store = Roo.data.Store
26519  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
26520  * xgrid.store.reader = Roo.data.JsonReader 
26521  * 
26522  * 
26523  * @constructor
26524  * Creates a new GridField
26525  * @param {Object} config Configuration options
26526  */
26527 Roo.form.GridField = function(config){
26528     Roo.form.GridField.superclass.constructor.call(this, config);
26529      
26530 };
26531
26532 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
26533     /**
26534      * @cfg {Number} width  - used to restrict width of grid..
26535      */
26536     width : 100,
26537     /**
26538      * @cfg {Number} height - used to restrict height of grid..
26539      */
26540     height : 50,
26541      /**
26542      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
26543          * 
26544          *}
26545      */
26546     xgrid : false, 
26547     /**
26548      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
26549      * {tag: "input", type: "checkbox", autocomplete: "off"})
26550      */
26551    // defaultAutoCreate : { tag: 'div' },
26552     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
26553     /**
26554      * @cfg {String} addTitle Text to include for adding a title.
26555      */
26556     addTitle : false,
26557     //
26558     onResize : function(){
26559         Roo.form.Field.superclass.onResize.apply(this, arguments);
26560     },
26561
26562     initEvents : function(){
26563         // Roo.form.Checkbox.superclass.initEvents.call(this);
26564         // has no events...
26565        
26566     },
26567
26568
26569     getResizeEl : function(){
26570         return this.wrap;
26571     },
26572
26573     getPositionEl : function(){
26574         return this.wrap;
26575     },
26576
26577     // private
26578     onRender : function(ct, position){
26579         
26580         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
26581         var style = this.style;
26582         delete this.style;
26583         
26584         Roo.form.GridField.superclass.onRender.call(this, ct, position);
26585         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
26586         this.viewEl = this.wrap.createChild({ tag: 'div' });
26587         if (style) {
26588             this.viewEl.applyStyles(style);
26589         }
26590         if (this.width) {
26591             this.viewEl.setWidth(this.width);
26592         }
26593         if (this.height) {
26594             this.viewEl.setHeight(this.height);
26595         }
26596         //if(this.inputValue !== undefined){
26597         //this.setValue(this.value);
26598         
26599         
26600         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
26601         
26602         
26603         this.grid.render();
26604         this.grid.getDataSource().on('remove', this.refreshValue, this);
26605         this.grid.getDataSource().on('update', this.refreshValue, this);
26606         this.grid.on('afteredit', this.refreshValue, this);
26607  
26608     },
26609      
26610     
26611     /**
26612      * Sets the value of the item. 
26613      * @param {String} either an object  or a string..
26614      */
26615     setValue : function(v){
26616         //this.value = v;
26617         v = v || []; // empty set..
26618         // this does not seem smart - it really only affects memoryproxy grids..
26619         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
26620             var ds = this.grid.getDataSource();
26621             // assumes a json reader..
26622             var data = {}
26623             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
26624             ds.loadData( data);
26625         }
26626         // clear selection so it does not get stale.
26627         if (this.grid.sm) { 
26628             this.grid.sm.clearSelections();
26629         }
26630         
26631         Roo.form.GridField.superclass.setValue.call(this, v);
26632         this.refreshValue();
26633         // should load data in the grid really....
26634     },
26635     
26636     // private
26637     refreshValue: function() {
26638          var val = [];
26639         this.grid.getDataSource().each(function(r) {
26640             val.push(r.data);
26641         });
26642         this.el.dom.value = Roo.encode(val);
26643     }
26644     
26645      
26646     
26647     
26648 });/*
26649  * Based on:
26650  * Ext JS Library 1.1.1
26651  * Copyright(c) 2006-2007, Ext JS, LLC.
26652  *
26653  * Originally Released Under LGPL - original licence link has changed is not relivant.
26654  *
26655  * Fork - LGPL
26656  * <script type="text/javascript">
26657  */
26658 /**
26659  * @class Roo.form.DisplayField
26660  * @extends Roo.form.Field
26661  * A generic Field to display non-editable data.
26662  * @cfg {Boolean} closable (true|false) default false
26663  * @constructor
26664  * Creates a new Display Field item.
26665  * @param {Object} config Configuration options
26666  */
26667 Roo.form.DisplayField = function(config){
26668     Roo.form.DisplayField.superclass.constructor.call(this, config);
26669     
26670     this.addEvents({
26671         /**
26672          * @event close
26673          * Fires after the click the close btn
26674              * @param {Roo.form.DisplayField} this
26675              */
26676         close : true
26677     });
26678 };
26679
26680 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
26681     inputType:      'hidden',
26682     allowBlank:     true,
26683     readOnly:         true,
26684     
26685  
26686     /**
26687      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
26688      */
26689     focusClass : undefined,
26690     /**
26691      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
26692      */
26693     fieldClass: 'x-form-field',
26694     
26695      /**
26696      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
26697      */
26698     valueRenderer: undefined,
26699     
26700     width: 100,
26701     /**
26702      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
26703      * {tag: "input", type: "checkbox", autocomplete: "off"})
26704      */
26705      
26706  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
26707  
26708     closable : false,
26709     
26710     onResize : function(){
26711         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
26712         
26713     },
26714
26715     initEvents : function(){
26716         // Roo.form.Checkbox.superclass.initEvents.call(this);
26717         // has no events...
26718         
26719         if(this.closable){
26720             this.closeEl.on('click', this.onClose, this);
26721         }
26722        
26723     },
26724
26725
26726     getResizeEl : function(){
26727         return this.wrap;
26728     },
26729
26730     getPositionEl : function(){
26731         return this.wrap;
26732     },
26733
26734     // private
26735     onRender : function(ct, position){
26736         
26737         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
26738         //if(this.inputValue !== undefined){
26739         this.wrap = this.el.wrap();
26740         
26741         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
26742         
26743         if(this.closable){
26744             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
26745         }
26746         
26747         if (this.bodyStyle) {
26748             this.viewEl.applyStyles(this.bodyStyle);
26749         }
26750         //this.viewEl.setStyle('padding', '2px');
26751         
26752         this.setValue(this.value);
26753         
26754     },
26755 /*
26756     // private
26757     initValue : Roo.emptyFn,
26758
26759   */
26760
26761         // private
26762     onClick : function(){
26763         
26764     },
26765
26766     /**
26767      * Sets the checked state of the checkbox.
26768      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
26769      */
26770     setValue : function(v){
26771         this.value = v;
26772         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
26773         // this might be called before we have a dom element..
26774         if (!this.viewEl) {
26775             return;
26776         }
26777         this.viewEl.dom.innerHTML = html;
26778         Roo.form.DisplayField.superclass.setValue.call(this, v);
26779
26780     },
26781     
26782     onClose : function(e)
26783     {
26784         e.preventDefault();
26785         
26786         this.fireEvent('close', this);
26787     }
26788 });/*
26789  * 
26790  * Licence- LGPL
26791  * 
26792  */
26793
26794 /**
26795  * @class Roo.form.DayPicker
26796  * @extends Roo.form.Field
26797  * A Day picker show [M] [T] [W] ....
26798  * @constructor
26799  * Creates a new Day Picker
26800  * @param {Object} config Configuration options
26801  */
26802 Roo.form.DayPicker= function(config){
26803     Roo.form.DayPicker.superclass.constructor.call(this, config);
26804      
26805 };
26806
26807 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
26808     /**
26809      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
26810      */
26811     focusClass : undefined,
26812     /**
26813      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
26814      */
26815     fieldClass: "x-form-field",
26816    
26817     /**
26818      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
26819      * {tag: "input", type: "checkbox", autocomplete: "off"})
26820      */
26821     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
26822     
26823    
26824     actionMode : 'viewEl', 
26825     //
26826     // private
26827  
26828     inputType : 'hidden',
26829     
26830      
26831     inputElement: false, // real input element?
26832     basedOn: false, // ????
26833     
26834     isFormField: true, // not sure where this is needed!!!!
26835
26836     onResize : function(){
26837         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
26838         if(!this.boxLabel){
26839             this.el.alignTo(this.wrap, 'c-c');
26840         }
26841     },
26842
26843     initEvents : function(){
26844         Roo.form.Checkbox.superclass.initEvents.call(this);
26845         this.el.on("click", this.onClick,  this);
26846         this.el.on("change", this.onClick,  this);
26847     },
26848
26849
26850     getResizeEl : function(){
26851         return this.wrap;
26852     },
26853
26854     getPositionEl : function(){
26855         return this.wrap;
26856     },
26857
26858     
26859     // private
26860     onRender : function(ct, position){
26861         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
26862        
26863         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
26864         
26865         var r1 = '<table><tr>';
26866         var r2 = '<tr class="x-form-daypick-icons">';
26867         for (var i=0; i < 7; i++) {
26868             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
26869             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
26870         }
26871         
26872         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
26873         viewEl.select('img').on('click', this.onClick, this);
26874         this.viewEl = viewEl;   
26875         
26876         
26877         // this will not work on Chrome!!!
26878         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
26879         this.el.on('propertychange', this.setFromHidden,  this);  //ie
26880         
26881         
26882           
26883
26884     },
26885
26886     // private
26887     initValue : Roo.emptyFn,
26888
26889     /**
26890      * Returns the checked state of the checkbox.
26891      * @return {Boolean} True if checked, else false
26892      */
26893     getValue : function(){
26894         return this.el.dom.value;
26895         
26896     },
26897
26898         // private
26899     onClick : function(e){ 
26900         //this.setChecked(!this.checked);
26901         Roo.get(e.target).toggleClass('x-menu-item-checked');
26902         this.refreshValue();
26903         //if(this.el.dom.checked != this.checked){
26904         //    this.setValue(this.el.dom.checked);
26905        // }
26906     },
26907     
26908     // private
26909     refreshValue : function()
26910     {
26911         var val = '';
26912         this.viewEl.select('img',true).each(function(e,i,n)  {
26913             val += e.is(".x-menu-item-checked") ? String(n) : '';
26914         });
26915         this.setValue(val, true);
26916     },
26917
26918     /**
26919      * Sets the checked state of the checkbox.
26920      * On is always based on a string comparison between inputValue and the param.
26921      * @param {Boolean/String} value - the value to set 
26922      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
26923      */
26924     setValue : function(v,suppressEvent){
26925         if (!this.el.dom) {
26926             return;
26927         }
26928         var old = this.el.dom.value ;
26929         this.el.dom.value = v;
26930         if (suppressEvent) {
26931             return ;
26932         }
26933          
26934         // update display..
26935         this.viewEl.select('img',true).each(function(e,i,n)  {
26936             
26937             var on = e.is(".x-menu-item-checked");
26938             var newv = v.indexOf(String(n)) > -1;
26939             if (on != newv) {
26940                 e.toggleClass('x-menu-item-checked');
26941             }
26942             
26943         });
26944         
26945         
26946         this.fireEvent('change', this, v, old);
26947         
26948         
26949     },
26950    
26951     // handle setting of hidden value by some other method!!?!?
26952     setFromHidden: function()
26953     {
26954         if(!this.el){
26955             return;
26956         }
26957         //console.log("SET FROM HIDDEN");
26958         //alert('setFrom hidden');
26959         this.setValue(this.el.dom.value);
26960     },
26961     
26962     onDestroy : function()
26963     {
26964         if(this.viewEl){
26965             Roo.get(this.viewEl).remove();
26966         }
26967          
26968         Roo.form.DayPicker.superclass.onDestroy.call(this);
26969     }
26970
26971 });/*
26972  * RooJS Library 1.1.1
26973  * Copyright(c) 2008-2011  Alan Knowles
26974  *
26975  * License - LGPL
26976  */
26977  
26978
26979 /**
26980  * @class Roo.form.ComboCheck
26981  * @extends Roo.form.ComboBox
26982  * A combobox for multiple select items.
26983  *
26984  * FIXME - could do with a reset button..
26985  * 
26986  * @constructor
26987  * Create a new ComboCheck
26988  * @param {Object} config Configuration options
26989  */
26990 Roo.form.ComboCheck = function(config){
26991     Roo.form.ComboCheck.superclass.constructor.call(this, config);
26992     // should verify some data...
26993     // like
26994     // hiddenName = required..
26995     // displayField = required
26996     // valudField == required
26997     var req= [ 'hiddenName', 'displayField', 'valueField' ];
26998     var _t = this;
26999     Roo.each(req, function(e) {
27000         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
27001             throw "Roo.form.ComboCheck : missing value for: " + e;
27002         }
27003     });
27004     
27005     
27006 };
27007
27008 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
27009      
27010      
27011     editable : false,
27012      
27013     selectedClass: 'x-menu-item-checked', 
27014     
27015     // private
27016     onRender : function(ct, position){
27017         var _t = this;
27018         
27019         
27020         
27021         if(!this.tpl){
27022             var cls = 'x-combo-list';
27023
27024             
27025             this.tpl =  new Roo.Template({
27026                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
27027                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
27028                    '<span>{' + this.displayField + '}</span>' +
27029                     '</div>' 
27030                 
27031             });
27032         }
27033  
27034         
27035         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
27036         this.view.singleSelect = false;
27037         this.view.multiSelect = true;
27038         this.view.toggleSelect = true;
27039         this.pageTb.add(new Roo.Toolbar.Fill(), {
27040             
27041             text: 'Done',
27042             handler: function()
27043             {
27044                 _t.collapse();
27045             }
27046         });
27047     },
27048     
27049     onViewOver : function(e, t){
27050         // do nothing...
27051         return;
27052         
27053     },
27054     
27055     onViewClick : function(doFocus,index){
27056         return;
27057         
27058     },
27059     select: function () {
27060         //Roo.log("SELECT CALLED");
27061     },
27062      
27063     selectByValue : function(xv, scrollIntoView){
27064         var ar = this.getValueArray();
27065         var sels = [];
27066         
27067         Roo.each(ar, function(v) {
27068             if(v === undefined || v === null){
27069                 return;
27070             }
27071             var r = this.findRecord(this.valueField, v);
27072             if(r){
27073                 sels.push(this.store.indexOf(r))
27074                 
27075             }
27076         },this);
27077         this.view.select(sels);
27078         return false;
27079     },
27080     
27081     
27082     
27083     onSelect : function(record, index){
27084        // Roo.log("onselect Called");
27085        // this is only called by the clear button now..
27086         this.view.clearSelections();
27087         this.setValue('[]');
27088         if (this.value != this.valueBefore) {
27089             this.fireEvent('change', this, this.value, this.valueBefore);
27090             this.valueBefore = this.value;
27091         }
27092     },
27093     getValueArray : function()
27094     {
27095         var ar = [] ;
27096         
27097         try {
27098             //Roo.log(this.value);
27099             if (typeof(this.value) == 'undefined') {
27100                 return [];
27101             }
27102             var ar = Roo.decode(this.value);
27103             return  ar instanceof Array ? ar : []; //?? valid?
27104             
27105         } catch(e) {
27106             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
27107             return [];
27108         }
27109          
27110     },
27111     expand : function ()
27112     {
27113         
27114         Roo.form.ComboCheck.superclass.expand.call(this);
27115         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
27116         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
27117         
27118
27119     },
27120     
27121     collapse : function(){
27122         Roo.form.ComboCheck.superclass.collapse.call(this);
27123         var sl = this.view.getSelectedIndexes();
27124         var st = this.store;
27125         var nv = [];
27126         var tv = [];
27127         var r;
27128         Roo.each(sl, function(i) {
27129             r = st.getAt(i);
27130             nv.push(r.get(this.valueField));
27131         },this);
27132         this.setValue(Roo.encode(nv));
27133         if (this.value != this.valueBefore) {
27134
27135             this.fireEvent('change', this, this.value, this.valueBefore);
27136             this.valueBefore = this.value;
27137         }
27138         
27139     },
27140     
27141     setValue : function(v){
27142         // Roo.log(v);
27143         this.value = v;
27144         
27145         var vals = this.getValueArray();
27146         var tv = [];
27147         Roo.each(vals, function(k) {
27148             var r = this.findRecord(this.valueField, k);
27149             if(r){
27150                 tv.push(r.data[this.displayField]);
27151             }else if(this.valueNotFoundText !== undefined){
27152                 tv.push( this.valueNotFoundText );
27153             }
27154         },this);
27155        // Roo.log(tv);
27156         
27157         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
27158         this.hiddenField.value = v;
27159         this.value = v;
27160     }
27161     
27162 });/*
27163  * Based on:
27164  * Ext JS Library 1.1.1
27165  * Copyright(c) 2006-2007, Ext JS, LLC.
27166  *
27167  * Originally Released Under LGPL - original licence link has changed is not relivant.
27168  *
27169  * Fork - LGPL
27170  * <script type="text/javascript">
27171  */
27172  
27173 /**
27174  * @class Roo.form.Signature
27175  * @extends Roo.form.Field
27176  * Signature field.  
27177  * @constructor
27178  * 
27179  * @param {Object} config Configuration options
27180  */
27181
27182 Roo.form.Signature = function(config){
27183     Roo.form.Signature.superclass.constructor.call(this, config);
27184     
27185     this.addEvents({// not in used??
27186          /**
27187          * @event confirm
27188          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
27189              * @param {Roo.form.Signature} combo This combo box
27190              */
27191         'confirm' : true,
27192         /**
27193          * @event reset
27194          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
27195              * @param {Roo.form.ComboBox} combo This combo box
27196              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
27197              */
27198         'reset' : true
27199     });
27200 };
27201
27202 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
27203     /**
27204      * @cfg {Object} labels Label to use when rendering a form.
27205      * defaults to 
27206      * labels : { 
27207      *      clear : "Clear",
27208      *      confirm : "Confirm"
27209      *  }
27210      */
27211     labels : { 
27212         clear : "Clear",
27213         confirm : "Confirm"
27214     },
27215     /**
27216      * @cfg {Number} width The signature panel width (defaults to 300)
27217      */
27218     width: 300,
27219     /**
27220      * @cfg {Number} height The signature panel height (defaults to 100)
27221      */
27222     height : 100,
27223     /**
27224      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
27225      */
27226     allowBlank : false,
27227     
27228     //private
27229     // {Object} signPanel The signature SVG panel element (defaults to {})
27230     signPanel : {},
27231     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
27232     isMouseDown : false,
27233     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
27234     isConfirmed : false,
27235     // {String} signatureTmp SVG mapping string (defaults to empty string)
27236     signatureTmp : '',
27237     
27238     
27239     defaultAutoCreate : { // modified by initCompnoent..
27240         tag: "input",
27241         type:"hidden"
27242     },
27243
27244     // private
27245     onRender : function(ct, position){
27246         
27247         Roo.form.Signature.superclass.onRender.call(this, ct, position);
27248         
27249         this.wrap = this.el.wrap({
27250             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
27251         });
27252         
27253         this.createToolbar(this);
27254         this.signPanel = this.wrap.createChild({
27255                 tag: 'div',
27256                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
27257             }, this.el
27258         );
27259             
27260         this.svgID = Roo.id();
27261         this.svgEl = this.signPanel.createChild({
27262               xmlns : 'http://www.w3.org/2000/svg',
27263               tag : 'svg',
27264               id : this.svgID + "-svg",
27265               width: this.width,
27266               height: this.height,
27267               viewBox: '0 0 '+this.width+' '+this.height,
27268               cn : [
27269                 {
27270                     tag: "rect",
27271                     id: this.svgID + "-svg-r",
27272                     width: this.width,
27273                     height: this.height,
27274                     fill: "#ffa"
27275                 },
27276                 {
27277                     tag: "line",
27278                     id: this.svgID + "-svg-l",
27279                     x1: "0", // start
27280                     y1: (this.height*0.8), // start set the line in 80% of height
27281                     x2: this.width, // end
27282                     y2: (this.height*0.8), // end set the line in 80% of height
27283                     'stroke': "#666",
27284                     'stroke-width': "1",
27285                     'stroke-dasharray': "3",
27286                     'shape-rendering': "crispEdges",
27287                     'pointer-events': "none"
27288                 },
27289                 {
27290                     tag: "path",
27291                     id: this.svgID + "-svg-p",
27292                     'stroke': "navy",
27293                     'stroke-width': "3",
27294                     'fill': "none",
27295                     'pointer-events': 'none'
27296                 }
27297               ]
27298         });
27299         this.createSVG();
27300         this.svgBox = this.svgEl.dom.getScreenCTM();
27301     },
27302     createSVG : function(){ 
27303         var svg = this.signPanel;
27304         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
27305         var t = this;
27306
27307         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
27308         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
27309         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
27310         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
27311         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
27312         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
27313         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
27314         
27315     },
27316     isTouchEvent : function(e){
27317         return e.type.match(/^touch/);
27318     },
27319     getCoords : function (e) {
27320         var pt    = this.svgEl.dom.createSVGPoint();
27321         pt.x = e.clientX; 
27322         pt.y = e.clientY;
27323         if (this.isTouchEvent(e)) {
27324             pt.x =  e.targetTouches[0].clientX;
27325             pt.y = e.targetTouches[0].clientY;
27326         }
27327         var a = this.svgEl.dom.getScreenCTM();
27328         var b = a.inverse();
27329         var mx = pt.matrixTransform(b);
27330         return mx.x + ',' + mx.y;
27331     },
27332     //mouse event headler 
27333     down : function (e) {
27334         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
27335         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
27336         
27337         this.isMouseDown = true;
27338         
27339         e.preventDefault();
27340     },
27341     move : function (e) {
27342         if (this.isMouseDown) {
27343             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
27344             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
27345         }
27346         
27347         e.preventDefault();
27348     },
27349     up : function (e) {
27350         this.isMouseDown = false;
27351         var sp = this.signatureTmp.split(' ');
27352         
27353         if(sp.length > 1){
27354             if(!sp[sp.length-2].match(/^L/)){
27355                 sp.pop();
27356                 sp.pop();
27357                 sp.push("");
27358                 this.signatureTmp = sp.join(" ");
27359             }
27360         }
27361         if(this.getValue() != this.signatureTmp){
27362             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27363             this.isConfirmed = false;
27364         }
27365         e.preventDefault();
27366     },
27367     
27368     /**
27369      * Protected method that will not generally be called directly. It
27370      * is called when the editor creates its toolbar. Override this method if you need to
27371      * add custom toolbar buttons.
27372      * @param {HtmlEditor} editor
27373      */
27374     createToolbar : function(editor){
27375          function btn(id, toggle, handler){
27376             var xid = fid + '-'+ id ;
27377             return {
27378                 id : xid,
27379                 cmd : id,
27380                 cls : 'x-btn-icon x-edit-'+id,
27381                 enableToggle:toggle !== false,
27382                 scope: editor, // was editor...
27383                 handler:handler||editor.relayBtnCmd,
27384                 clickEvent:'mousedown',
27385                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27386                 tabIndex:-1
27387             };
27388         }
27389         
27390         
27391         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
27392         this.tb = tb;
27393         this.tb.add(
27394            {
27395                 cls : ' x-signature-btn x-signature-'+id,
27396                 scope: editor, // was editor...
27397                 handler: this.reset,
27398                 clickEvent:'mousedown',
27399                 text: this.labels.clear
27400             },
27401             {
27402                  xtype : 'Fill',
27403                  xns: Roo.Toolbar
27404             }, 
27405             {
27406                 cls : '  x-signature-btn x-signature-'+id,
27407                 scope: editor, // was editor...
27408                 handler: this.confirmHandler,
27409                 clickEvent:'mousedown',
27410                 text: this.labels.confirm
27411             }
27412         );
27413     
27414     },
27415     //public
27416     /**
27417      * when user is clicked confirm then show this image.....
27418      * 
27419      * @return {String} Image Data URI
27420      */
27421     getImageDataURI : function(){
27422         var svg = this.svgEl.dom.parentNode.innerHTML;
27423         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
27424         return src; 
27425     },
27426     /**
27427      * 
27428      * @return {Boolean} this.isConfirmed
27429      */
27430     getConfirmed : function(){
27431         return this.isConfirmed;
27432     },
27433     /**
27434      * 
27435      * @return {Number} this.width
27436      */
27437     getWidth : function(){
27438         return this.width;
27439     },
27440     /**
27441      * 
27442      * @return {Number} this.height
27443      */
27444     getHeight : function(){
27445         return this.height;
27446     },
27447     // private
27448     getSignature : function(){
27449         return this.signatureTmp;
27450     },
27451     // private
27452     reset : function(){
27453         this.signatureTmp = '';
27454         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27455         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
27456         this.isConfirmed = false;
27457         Roo.form.Signature.superclass.reset.call(this);
27458     },
27459     setSignature : function(s){
27460         this.signatureTmp = s;
27461         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27462         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
27463         this.setValue(s);
27464         this.isConfirmed = false;
27465         Roo.form.Signature.superclass.reset.call(this);
27466     }, 
27467     test : function(){
27468 //        Roo.log(this.signPanel.dom.contentWindow.up())
27469     },
27470     //private
27471     setConfirmed : function(){
27472         
27473         
27474         
27475 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
27476     },
27477     // private
27478     confirmHandler : function(){
27479         if(!this.getSignature()){
27480             return;
27481         }
27482         
27483         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
27484         this.setValue(this.getSignature());
27485         this.isConfirmed = true;
27486         
27487         this.fireEvent('confirm', this);
27488     },
27489     // private
27490     // Subclasses should provide the validation implementation by overriding this
27491     validateValue : function(value){
27492         if(this.allowBlank){
27493             return true;
27494         }
27495         
27496         if(this.isConfirmed){
27497             return true;
27498         }
27499         return false;
27500     }
27501 });/*
27502  * Based on:
27503  * Ext JS Library 1.1.1
27504  * Copyright(c) 2006-2007, Ext JS, LLC.
27505  *
27506  * Originally Released Under LGPL - original licence link has changed is not relivant.
27507  *
27508  * Fork - LGPL
27509  * <script type="text/javascript">
27510  */
27511  
27512
27513 /**
27514  * @class Roo.form.ComboBox
27515  * @extends Roo.form.TriggerField
27516  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
27517  * @constructor
27518  * Create a new ComboBox.
27519  * @param {Object} config Configuration options
27520  */
27521 Roo.form.Select = function(config){
27522     Roo.form.Select.superclass.constructor.call(this, config);
27523      
27524 };
27525
27526 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
27527     /**
27528      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
27529      */
27530     /**
27531      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
27532      * rendering into an Roo.Editor, defaults to false)
27533      */
27534     /**
27535      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
27536      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
27537      */
27538     /**
27539      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
27540      */
27541     /**
27542      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
27543      * the dropdown list (defaults to undefined, with no header element)
27544      */
27545
27546      /**
27547      * @cfg {String/Roo.Template} tpl The template to use to render the output
27548      */
27549      
27550     // private
27551     defaultAutoCreate : {tag: "select"  },
27552     /**
27553      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
27554      */
27555     listWidth: undefined,
27556     /**
27557      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
27558      * mode = 'remote' or 'text' if mode = 'local')
27559      */
27560     displayField: undefined,
27561     /**
27562      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
27563      * mode = 'remote' or 'value' if mode = 'local'). 
27564      * Note: use of a valueField requires the user make a selection
27565      * in order for a value to be mapped.
27566      */
27567     valueField: undefined,
27568     
27569     
27570     /**
27571      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
27572      * field's data value (defaults to the underlying DOM element's name)
27573      */
27574     hiddenName: undefined,
27575     /**
27576      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
27577      */
27578     listClass: '',
27579     /**
27580      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
27581      */
27582     selectedClass: 'x-combo-selected',
27583     /**
27584      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
27585      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
27586      * which displays a downward arrow icon).
27587      */
27588     triggerClass : 'x-form-arrow-trigger',
27589     /**
27590      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
27591      */
27592     shadow:'sides',
27593     /**
27594      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
27595      * anchor positions (defaults to 'tl-bl')
27596      */
27597     listAlign: 'tl-bl?',
27598     /**
27599      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
27600      */
27601     maxHeight: 300,
27602     /**
27603      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
27604      * query specified by the allQuery config option (defaults to 'query')
27605      */
27606     triggerAction: 'query',
27607     /**
27608      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
27609      * (defaults to 4, does not apply if editable = false)
27610      */
27611     minChars : 4,
27612     /**
27613      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
27614      * delay (typeAheadDelay) if it matches a known value (defaults to false)
27615      */
27616     typeAhead: false,
27617     /**
27618      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
27619      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
27620      */
27621     queryDelay: 500,
27622     /**
27623      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
27624      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
27625      */
27626     pageSize: 0,
27627     /**
27628      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
27629      * when editable = true (defaults to false)
27630      */
27631     selectOnFocus:false,
27632     /**
27633      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
27634      */
27635     queryParam: 'query',
27636     /**
27637      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
27638      * when mode = 'remote' (defaults to 'Loading...')
27639      */
27640     loadingText: 'Loading...',
27641     /**
27642      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
27643      */
27644     resizable: false,
27645     /**
27646      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
27647      */
27648     handleHeight : 8,
27649     /**
27650      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
27651      * traditional select (defaults to true)
27652      */
27653     editable: true,
27654     /**
27655      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
27656      */
27657     allQuery: '',
27658     /**
27659      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
27660      */
27661     mode: 'remote',
27662     /**
27663      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
27664      * listWidth has a higher value)
27665      */
27666     minListWidth : 70,
27667     /**
27668      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
27669      * allow the user to set arbitrary text into the field (defaults to false)
27670      */
27671     forceSelection:false,
27672     /**
27673      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
27674      * if typeAhead = true (defaults to 250)
27675      */
27676     typeAheadDelay : 250,
27677     /**
27678      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
27679      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
27680      */
27681     valueNotFoundText : undefined,
27682     
27683     /**
27684      * @cfg {String} defaultValue The value displayed after loading the store.
27685      */
27686     defaultValue: '',
27687     
27688     /**
27689      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
27690      */
27691     blockFocus : false,
27692     
27693     /**
27694      * @cfg {Boolean} disableClear Disable showing of clear button.
27695      */
27696     disableClear : false,
27697     /**
27698      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
27699      */
27700     alwaysQuery : false,
27701     
27702     //private
27703     addicon : false,
27704     editicon: false,
27705     
27706     // element that contains real text value.. (when hidden is used..)
27707      
27708     // private
27709     onRender : function(ct, position){
27710         Roo.form.Field.prototype.onRender.call(this, ct, position);
27711         
27712         if(this.store){
27713             this.store.on('beforeload', this.onBeforeLoad, this);
27714             this.store.on('load', this.onLoad, this);
27715             this.store.on('loadexception', this.onLoadException, this);
27716             this.store.load({});
27717         }
27718         
27719         
27720         
27721     },
27722
27723     // private
27724     initEvents : function(){
27725         //Roo.form.ComboBox.superclass.initEvents.call(this);
27726  
27727     },
27728
27729     onDestroy : function(){
27730        
27731         if(this.store){
27732             this.store.un('beforeload', this.onBeforeLoad, this);
27733             this.store.un('load', this.onLoad, this);
27734             this.store.un('loadexception', this.onLoadException, this);
27735         }
27736         //Roo.form.ComboBox.superclass.onDestroy.call(this);
27737     },
27738
27739     // private
27740     fireKey : function(e){
27741         if(e.isNavKeyPress() && !this.list.isVisible()){
27742             this.fireEvent("specialkey", this, e);
27743         }
27744     },
27745
27746     // private
27747     onResize: function(w, h){
27748         
27749         return; 
27750     
27751         
27752     },
27753
27754     /**
27755      * Allow or prevent the user from directly editing the field text.  If false is passed,
27756      * the user will only be able to select from the items defined in the dropdown list.  This method
27757      * is the runtime equivalent of setting the 'editable' config option at config time.
27758      * @param {Boolean} value True to allow the user to directly edit the field text
27759      */
27760     setEditable : function(value){
27761          
27762     },
27763
27764     // private
27765     onBeforeLoad : function(){
27766         
27767         Roo.log("Select before load");
27768         return;
27769     
27770         this.innerList.update(this.loadingText ?
27771                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
27772         //this.restrictHeight();
27773         this.selectedIndex = -1;
27774     },
27775
27776     // private
27777     onLoad : function(){
27778
27779     
27780         var dom = this.el.dom;
27781         dom.innerHTML = '';
27782          var od = dom.ownerDocument;
27783          
27784         if (this.emptyText) {
27785             var op = od.createElement('option');
27786             op.setAttribute('value', '');
27787             op.innerHTML = String.format('{0}', this.emptyText);
27788             dom.appendChild(op);
27789         }
27790         if(this.store.getCount() > 0){
27791            
27792             var vf = this.valueField;
27793             var df = this.displayField;
27794             this.store.data.each(function(r) {
27795                 // which colmsn to use... testing - cdoe / title..
27796                 var op = od.createElement('option');
27797                 op.setAttribute('value', r.data[vf]);
27798                 op.innerHTML = String.format('{0}', r.data[df]);
27799                 dom.appendChild(op);
27800             });
27801             if (typeof(this.defaultValue != 'undefined')) {
27802                 this.setValue(this.defaultValue);
27803             }
27804             
27805              
27806         }else{
27807             //this.onEmptyResults();
27808         }
27809         //this.el.focus();
27810     },
27811     // private
27812     onLoadException : function()
27813     {
27814         dom.innerHTML = '';
27815             
27816         Roo.log("Select on load exception");
27817         return;
27818     
27819         this.collapse();
27820         Roo.log(this.store.reader.jsonData);
27821         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
27822             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
27823         }
27824         
27825         
27826     },
27827     // private
27828     onTypeAhead : function(){
27829          
27830     },
27831
27832     // private
27833     onSelect : function(record, index){
27834         Roo.log('on select?');
27835         return;
27836         if(this.fireEvent('beforeselect', this, record, index) !== false){
27837             this.setFromData(index > -1 ? record.data : false);
27838             this.collapse();
27839             this.fireEvent('select', this, record, index);
27840         }
27841     },
27842
27843     /**
27844      * Returns the currently selected field value or empty string if no value is set.
27845      * @return {String} value The selected value
27846      */
27847     getValue : function(){
27848         var dom = this.el.dom;
27849         this.value = dom.options[dom.selectedIndex].value;
27850         return this.value;
27851         
27852     },
27853
27854     /**
27855      * Clears any text/value currently set in the field
27856      */
27857     clearValue : function(){
27858         this.value = '';
27859         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
27860         
27861     },
27862
27863     /**
27864      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
27865      * will be displayed in the field.  If the value does not match the data value of an existing item,
27866      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
27867      * Otherwise the field will be blank (although the value will still be set).
27868      * @param {String} value The value to match
27869      */
27870     setValue : function(v){
27871         var d = this.el.dom;
27872         for (var i =0; i < d.options.length;i++) {
27873             if (v == d.options[i].value) {
27874                 d.selectedIndex = i;
27875                 this.value = v;
27876                 return;
27877             }
27878         }
27879         this.clearValue();
27880     },
27881     /**
27882      * @property {Object} the last set data for the element
27883      */
27884     
27885     lastData : false,
27886     /**
27887      * Sets the value of the field based on a object which is related to the record format for the store.
27888      * @param {Object} value the value to set as. or false on reset?
27889      */
27890     setFromData : function(o){
27891         Roo.log('setfrom data?');
27892          
27893         
27894         
27895     },
27896     // private
27897     reset : function(){
27898         this.clearValue();
27899     },
27900     // private
27901     findRecord : function(prop, value){
27902         
27903         return false;
27904     
27905         var record;
27906         if(this.store.getCount() > 0){
27907             this.store.each(function(r){
27908                 if(r.data[prop] == value){
27909                     record = r;
27910                     return false;
27911                 }
27912                 return true;
27913             });
27914         }
27915         return record;
27916     },
27917     
27918     getName: function()
27919     {
27920         // returns hidden if it's set..
27921         if (!this.rendered) {return ''};
27922         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
27923         
27924     },
27925      
27926
27927     
27928
27929     // private
27930     onEmptyResults : function(){
27931         Roo.log('empty results');
27932         //this.collapse();
27933     },
27934
27935     /**
27936      * Returns true if the dropdown list is expanded, else false.
27937      */
27938     isExpanded : function(){
27939         return false;
27940     },
27941
27942     /**
27943      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
27944      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
27945      * @param {String} value The data value of the item to select
27946      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
27947      * selected item if it is not currently in view (defaults to true)
27948      * @return {Boolean} True if the value matched an item in the list, else false
27949      */
27950     selectByValue : function(v, scrollIntoView){
27951         Roo.log('select By Value');
27952         return false;
27953     
27954         if(v !== undefined && v !== null){
27955             var r = this.findRecord(this.valueField || this.displayField, v);
27956             if(r){
27957                 this.select(this.store.indexOf(r), scrollIntoView);
27958                 return true;
27959             }
27960         }
27961         return false;
27962     },
27963
27964     /**
27965      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
27966      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
27967      * @param {Number} index The zero-based index of the list item to select
27968      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
27969      * selected item if it is not currently in view (defaults to true)
27970      */
27971     select : function(index, scrollIntoView){
27972         Roo.log('select ');
27973         return  ;
27974         
27975         this.selectedIndex = index;
27976         this.view.select(index);
27977         if(scrollIntoView !== false){
27978             var el = this.view.getNode(index);
27979             if(el){
27980                 this.innerList.scrollChildIntoView(el, false);
27981             }
27982         }
27983     },
27984
27985       
27986
27987     // private
27988     validateBlur : function(){
27989         
27990         return;
27991         
27992     },
27993
27994     // private
27995     initQuery : function(){
27996         this.doQuery(this.getRawValue());
27997     },
27998
27999     // private
28000     doForce : function(){
28001         if(this.el.dom.value.length > 0){
28002             this.el.dom.value =
28003                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
28004              
28005         }
28006     },
28007
28008     /**
28009      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
28010      * query allowing the query action to be canceled if needed.
28011      * @param {String} query The SQL query to execute
28012      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
28013      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
28014      * saved in the current store (defaults to false)
28015      */
28016     doQuery : function(q, forceAll){
28017         
28018         Roo.log('doQuery?');
28019         if(q === undefined || q === null){
28020             q = '';
28021         }
28022         var qe = {
28023             query: q,
28024             forceAll: forceAll,
28025             combo: this,
28026             cancel:false
28027         };
28028         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
28029             return false;
28030         }
28031         q = qe.query;
28032         forceAll = qe.forceAll;
28033         if(forceAll === true || (q.length >= this.minChars)){
28034             if(this.lastQuery != q || this.alwaysQuery){
28035                 this.lastQuery = q;
28036                 if(this.mode == 'local'){
28037                     this.selectedIndex = -1;
28038                     if(forceAll){
28039                         this.store.clearFilter();
28040                     }else{
28041                         this.store.filter(this.displayField, q);
28042                     }
28043                     this.onLoad();
28044                 }else{
28045                     this.store.baseParams[this.queryParam] = q;
28046                     this.store.load({
28047                         params: this.getParams(q)
28048                     });
28049                     this.expand();
28050                 }
28051             }else{
28052                 this.selectedIndex = -1;
28053                 this.onLoad();   
28054             }
28055         }
28056     },
28057
28058     // private
28059     getParams : function(q){
28060         var p = {};
28061         //p[this.queryParam] = q;
28062         if(this.pageSize){
28063             p.start = 0;
28064             p.limit = this.pageSize;
28065         }
28066         return p;
28067     },
28068
28069     /**
28070      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
28071      */
28072     collapse : function(){
28073         
28074     },
28075
28076     // private
28077     collapseIf : function(e){
28078         
28079     },
28080
28081     /**
28082      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
28083      */
28084     expand : function(){
28085         
28086     } ,
28087
28088     // private
28089      
28090
28091     /** 
28092     * @cfg {Boolean} grow 
28093     * @hide 
28094     */
28095     /** 
28096     * @cfg {Number} growMin 
28097     * @hide 
28098     */
28099     /** 
28100     * @cfg {Number} growMax 
28101     * @hide 
28102     */
28103     /**
28104      * @hide
28105      * @method autoSize
28106      */
28107     
28108     setWidth : function()
28109     {
28110         
28111     },
28112     getResizeEl : function(){
28113         return this.el;
28114     }
28115 });//<script type="text/javasscript">
28116  
28117
28118 /**
28119  * @class Roo.DDView
28120  * A DnD enabled version of Roo.View.
28121  * @param {Element/String} container The Element in which to create the View.
28122  * @param {String} tpl The template string used to create the markup for each element of the View
28123  * @param {Object} config The configuration properties. These include all the config options of
28124  * {@link Roo.View} plus some specific to this class.<br>
28125  * <p>
28126  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
28127  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
28128  * <p>
28129  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
28130 .x-view-drag-insert-above {
28131         border-top:1px dotted #3366cc;
28132 }
28133 .x-view-drag-insert-below {
28134         border-bottom:1px dotted #3366cc;
28135 }
28136 </code></pre>
28137  * 
28138  */
28139  
28140 Roo.DDView = function(container, tpl, config) {
28141     Roo.DDView.superclass.constructor.apply(this, arguments);
28142     this.getEl().setStyle("outline", "0px none");
28143     this.getEl().unselectable();
28144     if (this.dragGroup) {
28145                 this.setDraggable(this.dragGroup.split(","));
28146     }
28147     if (this.dropGroup) {
28148                 this.setDroppable(this.dropGroup.split(","));
28149     }
28150     if (this.deletable) {
28151         this.setDeletable();
28152     }
28153     this.isDirtyFlag = false;
28154         this.addEvents({
28155                 "drop" : true
28156         });
28157 };
28158
28159 Roo.extend(Roo.DDView, Roo.View, {
28160 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
28161 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
28162 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
28163 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
28164
28165         isFormField: true,
28166
28167         reset: Roo.emptyFn,
28168         
28169         clearInvalid: Roo.form.Field.prototype.clearInvalid,
28170
28171         validate: function() {
28172                 return true;
28173         },
28174         
28175         destroy: function() {
28176                 this.purgeListeners();
28177                 this.getEl.removeAllListeners();
28178                 this.getEl().remove();
28179                 if (this.dragZone) {
28180                         if (this.dragZone.destroy) {
28181                                 this.dragZone.destroy();
28182                         }
28183                 }
28184                 if (this.dropZone) {
28185                         if (this.dropZone.destroy) {
28186                                 this.dropZone.destroy();
28187                         }
28188                 }
28189         },
28190
28191 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
28192         getName: function() {
28193                 return this.name;
28194         },
28195
28196 /**     Loads the View from a JSON string representing the Records to put into the Store. */
28197         setValue: function(v) {
28198                 if (!this.store) {
28199                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
28200                 }
28201                 var data = {};
28202                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
28203                 this.store.proxy = new Roo.data.MemoryProxy(data);
28204                 this.store.load();
28205         },
28206
28207 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
28208         getValue: function() {
28209                 var result = '(';
28210                 this.store.each(function(rec) {
28211                         result += rec.id + ',';
28212                 });
28213                 return result.substr(0, result.length - 1) + ')';
28214         },
28215         
28216         getIds: function() {
28217                 var i = 0, result = new Array(this.store.getCount());
28218                 this.store.each(function(rec) {
28219                         result[i++] = rec.id;
28220                 });
28221                 return result;
28222         },
28223         
28224         isDirty: function() {
28225                 return this.isDirtyFlag;
28226         },
28227
28228 /**
28229  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
28230  *      whole Element becomes the target, and this causes the drop gesture to append.
28231  */
28232     getTargetFromEvent : function(e) {
28233                 var target = e.getTarget();
28234                 while ((target !== null) && (target.parentNode != this.el.dom)) {
28235                 target = target.parentNode;
28236                 }
28237                 if (!target) {
28238                         target = this.el.dom.lastChild || this.el.dom;
28239                 }
28240                 return target;
28241     },
28242
28243 /**
28244  *      Create the drag data which consists of an object which has the property "ddel" as
28245  *      the drag proxy element. 
28246  */
28247     getDragData : function(e) {
28248         var target = this.findItemFromChild(e.getTarget());
28249                 if(target) {
28250                         this.handleSelection(e);
28251                         var selNodes = this.getSelectedNodes();
28252             var dragData = {
28253                 source: this,
28254                 copy: this.copy || (this.allowCopy && e.ctrlKey),
28255                 nodes: selNodes,
28256                 records: []
28257                         };
28258                         var selectedIndices = this.getSelectedIndexes();
28259                         for (var i = 0; i < selectedIndices.length; i++) {
28260                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
28261                         }
28262                         if (selNodes.length == 1) {
28263                                 dragData.ddel = target.cloneNode(true); // the div element
28264                         } else {
28265                                 var div = document.createElement('div'); // create the multi element drag "ghost"
28266                                 div.className = 'multi-proxy';
28267                                 for (var i = 0, len = selNodes.length; i < len; i++) {
28268                                         div.appendChild(selNodes[i].cloneNode(true));
28269                                 }
28270                                 dragData.ddel = div;
28271                         }
28272             //console.log(dragData)
28273             //console.log(dragData.ddel.innerHTML)
28274                         return dragData;
28275                 }
28276         //console.log('nodragData')
28277                 return false;
28278     },
28279     
28280 /**     Specify to which ddGroup items in this DDView may be dragged. */
28281     setDraggable: function(ddGroup) {
28282         if (ddGroup instanceof Array) {
28283                 Roo.each(ddGroup, this.setDraggable, this);
28284                 return;
28285         }
28286         if (this.dragZone) {
28287                 this.dragZone.addToGroup(ddGroup);
28288         } else {
28289                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
28290                                 containerScroll: true,
28291                                 ddGroup: ddGroup 
28292
28293                         });
28294 //                      Draggability implies selection. DragZone's mousedown selects the element.
28295                         if (!this.multiSelect) { this.singleSelect = true; }
28296
28297 //                      Wire the DragZone's handlers up to methods in *this*
28298                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
28299                 }
28300     },
28301
28302 /**     Specify from which ddGroup this DDView accepts drops. */
28303     setDroppable: function(ddGroup) {
28304         if (ddGroup instanceof Array) {
28305                 Roo.each(ddGroup, this.setDroppable, this);
28306                 return;
28307         }
28308         if (this.dropZone) {
28309                 this.dropZone.addToGroup(ddGroup);
28310         } else {
28311                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
28312                                 containerScroll: true,
28313                                 ddGroup: ddGroup
28314                         });
28315
28316 //                      Wire the DropZone's handlers up to methods in *this*
28317                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
28318                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
28319                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
28320                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
28321                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
28322                 }
28323     },
28324
28325 /**     Decide whether to drop above or below a View node. */
28326     getDropPoint : function(e, n, dd){
28327         if (n == this.el.dom) { return "above"; }
28328                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
28329                 var c = t + (b - t) / 2;
28330                 var y = Roo.lib.Event.getPageY(e);
28331                 if(y <= c) {
28332                         return "above";
28333                 }else{
28334                         return "below";
28335                 }
28336     },
28337
28338     onNodeEnter : function(n, dd, e, data){
28339                 return false;
28340     },
28341     
28342     onNodeOver : function(n, dd, e, data){
28343                 var pt = this.getDropPoint(e, n, dd);
28344                 // set the insert point style on the target node
28345                 var dragElClass = this.dropNotAllowed;
28346                 if (pt) {
28347                         var targetElClass;
28348                         if (pt == "above"){
28349                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
28350                                 targetElClass = "x-view-drag-insert-above";
28351                         } else {
28352                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
28353                                 targetElClass = "x-view-drag-insert-below";
28354                         }
28355                         if (this.lastInsertClass != targetElClass){
28356                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
28357                                 this.lastInsertClass = targetElClass;
28358                         }
28359                 }
28360                 return dragElClass;
28361         },
28362
28363     onNodeOut : function(n, dd, e, data){
28364                 this.removeDropIndicators(n);
28365     },
28366
28367     onNodeDrop : function(n, dd, e, data){
28368         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
28369                 return false;
28370         }
28371         var pt = this.getDropPoint(e, n, dd);
28372                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
28373                 if (pt == "below") { insertAt++; }
28374                 for (var i = 0; i < data.records.length; i++) {
28375                         var r = data.records[i];
28376                         var dup = this.store.getById(r.id);
28377                         if (dup && (dd != this.dragZone)) {
28378                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
28379                         } else {
28380                                 if (data.copy) {
28381                                         this.store.insert(insertAt++, r.copy());
28382                                 } else {
28383                                         data.source.isDirtyFlag = true;
28384                                         r.store.remove(r);
28385                                         this.store.insert(insertAt++, r);
28386                                 }
28387                                 this.isDirtyFlag = true;
28388                         }
28389                 }
28390                 this.dragZone.cachedTarget = null;
28391                 return true;
28392     },
28393
28394     removeDropIndicators : function(n){
28395                 if(n){
28396                         Roo.fly(n).removeClass([
28397                                 "x-view-drag-insert-above",
28398                                 "x-view-drag-insert-below"]);
28399                         this.lastInsertClass = "_noclass";
28400                 }
28401     },
28402
28403 /**
28404  *      Utility method. Add a delete option to the DDView's context menu.
28405  *      @param {String} imageUrl The URL of the "delete" icon image.
28406  */
28407         setDeletable: function(imageUrl) {
28408                 if (!this.singleSelect && !this.multiSelect) {
28409                         this.singleSelect = true;
28410                 }
28411                 var c = this.getContextMenu();
28412                 this.contextMenu.on("itemclick", function(item) {
28413                         switch (item.id) {
28414                                 case "delete":
28415                                         this.remove(this.getSelectedIndexes());
28416                                         break;
28417                         }
28418                 }, this);
28419                 this.contextMenu.add({
28420                         icon: imageUrl,
28421                         id: "delete",
28422                         text: 'Delete'
28423                 });
28424         },
28425         
28426 /**     Return the context menu for this DDView. */
28427         getContextMenu: function() {
28428                 if (!this.contextMenu) {
28429 //                      Create the View's context menu
28430                         this.contextMenu = new Roo.menu.Menu({
28431                                 id: this.id + "-contextmenu"
28432                         });
28433                         this.el.on("contextmenu", this.showContextMenu, this);
28434                 }
28435                 return this.contextMenu;
28436         },
28437         
28438         disableContextMenu: function() {
28439                 if (this.contextMenu) {
28440                         this.el.un("contextmenu", this.showContextMenu, this);
28441                 }
28442         },
28443
28444         showContextMenu: function(e, item) {
28445         item = this.findItemFromChild(e.getTarget());
28446                 if (item) {
28447                         e.stopEvent();
28448                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
28449                         this.contextMenu.showAt(e.getXY());
28450             }
28451     },
28452
28453 /**
28454  *      Remove {@link Roo.data.Record}s at the specified indices.
28455  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
28456  */
28457     remove: function(selectedIndices) {
28458                 selectedIndices = [].concat(selectedIndices);
28459                 for (var i = 0; i < selectedIndices.length; i++) {
28460                         var rec = this.store.getAt(selectedIndices[i]);
28461                         this.store.remove(rec);
28462                 }
28463     },
28464
28465 /**
28466  *      Double click fires the event, but also, if this is draggable, and there is only one other
28467  *      related DropZone, it transfers the selected node.
28468  */
28469     onDblClick : function(e){
28470         var item = this.findItemFromChild(e.getTarget());
28471         if(item){
28472             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
28473                 return false;
28474             }
28475             if (this.dragGroup) {
28476                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
28477                     while (targets.indexOf(this.dropZone) > -1) {
28478                             targets.remove(this.dropZone);
28479                                 }
28480                     if (targets.length == 1) {
28481                                         this.dragZone.cachedTarget = null;
28482                         var el = Roo.get(targets[0].getEl());
28483                         var box = el.getBox(true);
28484                         targets[0].onNodeDrop(el.dom, {
28485                                 target: el.dom,
28486                                 xy: [box.x, box.y + box.height - 1]
28487                         }, null, this.getDragData(e));
28488                     }
28489                 }
28490         }
28491     },
28492     
28493     handleSelection: function(e) {
28494                 this.dragZone.cachedTarget = null;
28495         var item = this.findItemFromChild(e.getTarget());
28496         if (!item) {
28497                 this.clearSelections(true);
28498                 return;
28499         }
28500                 if (item && (this.multiSelect || this.singleSelect)){
28501                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
28502                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
28503                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
28504                                 this.unselect(item);
28505                         } else {
28506                                 this.select(item, this.multiSelect && e.ctrlKey);
28507                                 this.lastSelection = item;
28508                         }
28509                 }
28510     },
28511
28512     onItemClick : function(item, index, e){
28513                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
28514                         return false;
28515                 }
28516                 return true;
28517     },
28518
28519     unselect : function(nodeInfo, suppressEvent){
28520                 var node = this.getNode(nodeInfo);
28521                 if(node && this.isSelected(node)){
28522                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28523                                 Roo.fly(node).removeClass(this.selectedClass);
28524                                 this.selections.remove(node);
28525                                 if(!suppressEvent){
28526                                         this.fireEvent("selectionchange", this, this.selections);
28527                                 }
28528                         }
28529                 }
28530     }
28531 });
28532 /*
28533  * Based on:
28534  * Ext JS Library 1.1.1
28535  * Copyright(c) 2006-2007, Ext JS, LLC.
28536  *
28537  * Originally Released Under LGPL - original licence link has changed is not relivant.
28538  *
28539  * Fork - LGPL
28540  * <script type="text/javascript">
28541  */
28542  
28543 /**
28544  * @class Roo.LayoutManager
28545  * @extends Roo.util.Observable
28546  * Base class for layout managers.
28547  */
28548 Roo.LayoutManager = function(container, config){
28549     Roo.LayoutManager.superclass.constructor.call(this);
28550     this.el = Roo.get(container);
28551     // ie scrollbar fix
28552     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
28553         document.body.scroll = "no";
28554     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
28555         this.el.position('relative');
28556     }
28557     this.id = this.el.id;
28558     this.el.addClass("x-layout-container");
28559     /** false to disable window resize monitoring @type Boolean */
28560     this.monitorWindowResize = true;
28561     this.regions = {};
28562     this.addEvents({
28563         /**
28564          * @event layout
28565          * Fires when a layout is performed. 
28566          * @param {Roo.LayoutManager} this
28567          */
28568         "layout" : true,
28569         /**
28570          * @event regionresized
28571          * Fires when the user resizes a region. 
28572          * @param {Roo.LayoutRegion} region The resized region
28573          * @param {Number} newSize The new size (width for east/west, height for north/south)
28574          */
28575         "regionresized" : true,
28576         /**
28577          * @event regioncollapsed
28578          * Fires when a region is collapsed. 
28579          * @param {Roo.LayoutRegion} region The collapsed region
28580          */
28581         "regioncollapsed" : true,
28582         /**
28583          * @event regionexpanded
28584          * Fires when a region is expanded.  
28585          * @param {Roo.LayoutRegion} region The expanded region
28586          */
28587         "regionexpanded" : true
28588     });
28589     this.updating = false;
28590     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
28591 };
28592
28593 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
28594     /**
28595      * Returns true if this layout is currently being updated
28596      * @return {Boolean}
28597      */
28598     isUpdating : function(){
28599         return this.updating; 
28600     },
28601     
28602     /**
28603      * Suspend the LayoutManager from doing auto-layouts while
28604      * making multiple add or remove calls
28605      */
28606     beginUpdate : function(){
28607         this.updating = true;    
28608     },
28609     
28610     /**
28611      * Restore auto-layouts and optionally disable the manager from performing a layout
28612      * @param {Boolean} noLayout true to disable a layout update 
28613      */
28614     endUpdate : function(noLayout){
28615         this.updating = false;
28616         if(!noLayout){
28617             this.layout();
28618         }    
28619     },
28620     
28621     layout: function(){
28622         
28623     },
28624     
28625     onRegionResized : function(region, newSize){
28626         this.fireEvent("regionresized", region, newSize);
28627         this.layout();
28628     },
28629     
28630     onRegionCollapsed : function(region){
28631         this.fireEvent("regioncollapsed", region);
28632     },
28633     
28634     onRegionExpanded : function(region){
28635         this.fireEvent("regionexpanded", region);
28636     },
28637         
28638     /**
28639      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
28640      * performs box-model adjustments.
28641      * @return {Object} The size as an object {width: (the width), height: (the height)}
28642      */
28643     getViewSize : function(){
28644         var size;
28645         if(this.el.dom != document.body){
28646             size = this.el.getSize();
28647         }else{
28648             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
28649         }
28650         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
28651         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
28652         return size;
28653     },
28654     
28655     /**
28656      * Returns the Element this layout is bound to.
28657      * @return {Roo.Element}
28658      */
28659     getEl : function(){
28660         return this.el;
28661     },
28662     
28663     /**
28664      * Returns the specified region.
28665      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
28666      * @return {Roo.LayoutRegion}
28667      */
28668     getRegion : function(target){
28669         return this.regions[target.toLowerCase()];
28670     },
28671     
28672     onWindowResize : function(){
28673         if(this.monitorWindowResize){
28674             this.layout();
28675         }
28676     }
28677 });/*
28678  * Based on:
28679  * Ext JS Library 1.1.1
28680  * Copyright(c) 2006-2007, Ext JS, LLC.
28681  *
28682  * Originally Released Under LGPL - original licence link has changed is not relivant.
28683  *
28684  * Fork - LGPL
28685  * <script type="text/javascript">
28686  */
28687 /**
28688  * @class Roo.BorderLayout
28689  * @extends Roo.LayoutManager
28690  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
28691  * please see: <br><br>
28692  * <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>
28693  * <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>
28694  * Example:
28695  <pre><code>
28696  var layout = new Roo.BorderLayout(document.body, {
28697     north: {
28698         initialSize: 25,
28699         titlebar: false
28700     },
28701     west: {
28702         split:true,
28703         initialSize: 200,
28704         minSize: 175,
28705         maxSize: 400,
28706         titlebar: true,
28707         collapsible: true
28708     },
28709     east: {
28710         split:true,
28711         initialSize: 202,
28712         minSize: 175,
28713         maxSize: 400,
28714         titlebar: true,
28715         collapsible: true
28716     },
28717     south: {
28718         split:true,
28719         initialSize: 100,
28720         minSize: 100,
28721         maxSize: 200,
28722         titlebar: true,
28723         collapsible: true
28724     },
28725     center: {
28726         titlebar: true,
28727         autoScroll:true,
28728         resizeTabs: true,
28729         minTabWidth: 50,
28730         preferredTabWidth: 150
28731     }
28732 });
28733
28734 // shorthand
28735 var CP = Roo.ContentPanel;
28736
28737 layout.beginUpdate();
28738 layout.add("north", new CP("north", "North"));
28739 layout.add("south", new CP("south", {title: "South", closable: true}));
28740 layout.add("west", new CP("west", {title: "West"}));
28741 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
28742 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
28743 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
28744 layout.getRegion("center").showPanel("center1");
28745 layout.endUpdate();
28746 </code></pre>
28747
28748 <b>The container the layout is rendered into can be either the body element or any other element.
28749 If it is not the body element, the container needs to either be an absolute positioned element,
28750 or you will need to add "position:relative" to the css of the container.  You will also need to specify
28751 the container size if it is not the body element.</b>
28752
28753 * @constructor
28754 * Create a new BorderLayout
28755 * @param {String/HTMLElement/Element} container The container this layout is bound to
28756 * @param {Object} config Configuration options
28757  */
28758 Roo.BorderLayout = function(container, config){
28759     config = config || {};
28760     Roo.BorderLayout.superclass.constructor.call(this, container, config);
28761     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
28762     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
28763         var target = this.factory.validRegions[i];
28764         if(config[target]){
28765             this.addRegion(target, config[target]);
28766         }
28767     }
28768 };
28769
28770 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
28771     /**
28772      * Creates and adds a new region if it doesn't already exist.
28773      * @param {String} target The target region key (north, south, east, west or center).
28774      * @param {Object} config The regions config object
28775      * @return {BorderLayoutRegion} The new region
28776      */
28777     addRegion : function(target, config){
28778         if(!this.regions[target]){
28779             var r = this.factory.create(target, this, config);
28780             this.bindRegion(target, r);
28781         }
28782         return this.regions[target];
28783     },
28784
28785     // private (kinda)
28786     bindRegion : function(name, r){
28787         this.regions[name] = r;
28788         r.on("visibilitychange", this.layout, this);
28789         r.on("paneladded", this.layout, this);
28790         r.on("panelremoved", this.layout, this);
28791         r.on("invalidated", this.layout, this);
28792         r.on("resized", this.onRegionResized, this);
28793         r.on("collapsed", this.onRegionCollapsed, this);
28794         r.on("expanded", this.onRegionExpanded, this);
28795     },
28796
28797     /**
28798      * Performs a layout update.
28799      */
28800     layout : function(){
28801         if(this.updating) {
28802             return;
28803         }
28804         var size = this.getViewSize();
28805         var w = size.width;
28806         var h = size.height;
28807         var centerW = w;
28808         var centerH = h;
28809         var centerY = 0;
28810         var centerX = 0;
28811         //var x = 0, y = 0;
28812
28813         var rs = this.regions;
28814         var north = rs["north"];
28815         var south = rs["south"]; 
28816         var west = rs["west"];
28817         var east = rs["east"];
28818         var center = rs["center"];
28819         //if(this.hideOnLayout){ // not supported anymore
28820             //c.el.setStyle("display", "none");
28821         //}
28822         if(north && north.isVisible()){
28823             var b = north.getBox();
28824             var m = north.getMargins();
28825             b.width = w - (m.left+m.right);
28826             b.x = m.left;
28827             b.y = m.top;
28828             centerY = b.height + b.y + m.bottom;
28829             centerH -= centerY;
28830             north.updateBox(this.safeBox(b));
28831         }
28832         if(south && south.isVisible()){
28833             var b = south.getBox();
28834             var m = south.getMargins();
28835             b.width = w - (m.left+m.right);
28836             b.x = m.left;
28837             var totalHeight = (b.height + m.top + m.bottom);
28838             b.y = h - totalHeight + m.top;
28839             centerH -= totalHeight;
28840             south.updateBox(this.safeBox(b));
28841         }
28842         if(west && west.isVisible()){
28843             var b = west.getBox();
28844             var m = west.getMargins();
28845             b.height = centerH - (m.top+m.bottom);
28846             b.x = m.left;
28847             b.y = centerY + m.top;
28848             var totalWidth = (b.width + m.left + m.right);
28849             centerX += totalWidth;
28850             centerW -= totalWidth;
28851             west.updateBox(this.safeBox(b));
28852         }
28853         if(east && east.isVisible()){
28854             var b = east.getBox();
28855             var m = east.getMargins();
28856             b.height = centerH - (m.top+m.bottom);
28857             var totalWidth = (b.width + m.left + m.right);
28858             b.x = w - totalWidth + m.left;
28859             b.y = centerY + m.top;
28860             centerW -= totalWidth;
28861             east.updateBox(this.safeBox(b));
28862         }
28863         if(center){
28864             var m = center.getMargins();
28865             var centerBox = {
28866                 x: centerX + m.left,
28867                 y: centerY + m.top,
28868                 width: centerW - (m.left+m.right),
28869                 height: centerH - (m.top+m.bottom)
28870             };
28871             //if(this.hideOnLayout){
28872                 //center.el.setStyle("display", "block");
28873             //}
28874             center.updateBox(this.safeBox(centerBox));
28875         }
28876         this.el.repaint();
28877         this.fireEvent("layout", this);
28878     },
28879
28880     // private
28881     safeBox : function(box){
28882         box.width = Math.max(0, box.width);
28883         box.height = Math.max(0, box.height);
28884         return box;
28885     },
28886
28887     /**
28888      * Adds a ContentPanel (or subclass) to this layout.
28889      * @param {String} target The target region key (north, south, east, west or center).
28890      * @param {Roo.ContentPanel} panel The panel to add
28891      * @return {Roo.ContentPanel} The added panel
28892      */
28893     add : function(target, panel){
28894          
28895         target = target.toLowerCase();
28896         return this.regions[target].add(panel);
28897     },
28898
28899     /**
28900      * Remove a ContentPanel (or subclass) to this layout.
28901      * @param {String} target The target region key (north, south, east, west or center).
28902      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
28903      * @return {Roo.ContentPanel} The removed panel
28904      */
28905     remove : function(target, panel){
28906         target = target.toLowerCase();
28907         return this.regions[target].remove(panel);
28908     },
28909
28910     /**
28911      * Searches all regions for a panel with the specified id
28912      * @param {String} panelId
28913      * @return {Roo.ContentPanel} The panel or null if it wasn't found
28914      */
28915     findPanel : function(panelId){
28916         var rs = this.regions;
28917         for(var target in rs){
28918             if(typeof rs[target] != "function"){
28919                 var p = rs[target].getPanel(panelId);
28920                 if(p){
28921                     return p;
28922                 }
28923             }
28924         }
28925         return null;
28926     },
28927
28928     /**
28929      * Searches all regions for a panel with the specified id and activates (shows) it.
28930      * @param {String/ContentPanel} panelId The panels id or the panel itself
28931      * @return {Roo.ContentPanel} The shown panel or null
28932      */
28933     showPanel : function(panelId) {
28934       var rs = this.regions;
28935       for(var target in rs){
28936          var r = rs[target];
28937          if(typeof r != "function"){
28938             if(r.hasPanel(panelId)){
28939                return r.showPanel(panelId);
28940             }
28941          }
28942       }
28943       return null;
28944    },
28945
28946    /**
28947      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
28948      * @param {Roo.state.Provider} provider (optional) An alternate state provider
28949      */
28950     restoreState : function(provider){
28951         if(!provider){
28952             provider = Roo.state.Manager;
28953         }
28954         var sm = new Roo.LayoutStateManager();
28955         sm.init(this, provider);
28956     },
28957
28958     /**
28959      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
28960      * object should contain properties for each region to add ContentPanels to, and each property's value should be
28961      * a valid ContentPanel config object.  Example:
28962      * <pre><code>
28963 // Create the main layout
28964 var layout = new Roo.BorderLayout('main-ct', {
28965     west: {
28966         split:true,
28967         minSize: 175,
28968         titlebar: true
28969     },
28970     center: {
28971         title:'Components'
28972     }
28973 }, 'main-ct');
28974
28975 // Create and add multiple ContentPanels at once via configs
28976 layout.batchAdd({
28977    west: {
28978        id: 'source-files',
28979        autoCreate:true,
28980        title:'Ext Source Files',
28981        autoScroll:true,
28982        fitToFrame:true
28983    },
28984    center : {
28985        el: cview,
28986        autoScroll:true,
28987        fitToFrame:true,
28988        toolbar: tb,
28989        resizeEl:'cbody'
28990    }
28991 });
28992 </code></pre>
28993      * @param {Object} regions An object containing ContentPanel configs by region name
28994      */
28995     batchAdd : function(regions){
28996         this.beginUpdate();
28997         for(var rname in regions){
28998             var lr = this.regions[rname];
28999             if(lr){
29000                 this.addTypedPanels(lr, regions[rname]);
29001             }
29002         }
29003         this.endUpdate();
29004     },
29005
29006     // private
29007     addTypedPanels : function(lr, ps){
29008         if(typeof ps == 'string'){
29009             lr.add(new Roo.ContentPanel(ps));
29010         }
29011         else if(ps instanceof Array){
29012             for(var i =0, len = ps.length; i < len; i++){
29013                 this.addTypedPanels(lr, ps[i]);
29014             }
29015         }
29016         else if(!ps.events){ // raw config?
29017             var el = ps.el;
29018             delete ps.el; // prevent conflict
29019             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
29020         }
29021         else {  // panel object assumed!
29022             lr.add(ps);
29023         }
29024     },
29025     /**
29026      * Adds a xtype elements to the layout.
29027      * <pre><code>
29028
29029 layout.addxtype({
29030        xtype : 'ContentPanel',
29031        region: 'west',
29032        items: [ .... ]
29033    }
29034 );
29035
29036 layout.addxtype({
29037         xtype : 'NestedLayoutPanel',
29038         region: 'west',
29039         layout: {
29040            center: { },
29041            west: { }   
29042         },
29043         items : [ ... list of content panels or nested layout panels.. ]
29044    }
29045 );
29046 </code></pre>
29047      * @param {Object} cfg Xtype definition of item to add.
29048      */
29049     addxtype : function(cfg)
29050     {
29051         // basically accepts a pannel...
29052         // can accept a layout region..!?!?
29053         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
29054         
29055         if (!cfg.xtype.match(/Panel$/)) {
29056             return false;
29057         }
29058         var ret = false;
29059         
29060         if (typeof(cfg.region) == 'undefined') {
29061             Roo.log("Failed to add Panel, region was not set");
29062             Roo.log(cfg);
29063             return false;
29064         }
29065         var region = cfg.region;
29066         delete cfg.region;
29067         
29068           
29069         var xitems = [];
29070         if (cfg.items) {
29071             xitems = cfg.items;
29072             delete cfg.items;
29073         }
29074         var nb = false;
29075         
29076         switch(cfg.xtype) 
29077         {
29078             case 'ContentPanel':  // ContentPanel (el, cfg)
29079             case 'ScrollPanel':  // ContentPanel (el, cfg)
29080             case 'ViewPanel': 
29081                 if(cfg.autoCreate) {
29082                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29083                 } else {
29084                     var el = this.el.createChild();
29085                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
29086                 }
29087                 
29088                 this.add(region, ret);
29089                 break;
29090             
29091             
29092             case 'TreePanel': // our new panel!
29093                 cfg.el = this.el.createChild();
29094                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29095                 this.add(region, ret);
29096                 break;
29097             
29098             case 'NestedLayoutPanel': 
29099                 // create a new Layout (which is  a Border Layout...
29100                 var el = this.el.createChild();
29101                 var clayout = cfg.layout;
29102                 delete cfg.layout;
29103                 clayout.items   = clayout.items  || [];
29104                 // replace this exitems with the clayout ones..
29105                 xitems = clayout.items;
29106                  
29107                 
29108                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
29109                     cfg.background = false;
29110                 }
29111                 var layout = new Roo.BorderLayout(el, clayout);
29112                 
29113                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
29114                 //console.log('adding nested layout panel '  + cfg.toSource());
29115                 this.add(region, ret);
29116                 nb = {}; /// find first...
29117                 break;
29118                 
29119             case 'GridPanel': 
29120             
29121                 // needs grid and region
29122                 
29123                 //var el = this.getRegion(region).el.createChild();
29124                 var el = this.el.createChild();
29125                 // create the grid first...
29126                 
29127                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
29128                 delete cfg.grid;
29129                 if (region == 'center' && this.active ) {
29130                     cfg.background = false;
29131                 }
29132                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
29133                 
29134                 this.add(region, ret);
29135                 if (cfg.background) {
29136                     ret.on('activate', function(gp) {
29137                         if (!gp.grid.rendered) {
29138                             gp.grid.render();
29139                         }
29140                     });
29141                 } else {
29142                     grid.render();
29143                 }
29144                 break;
29145            
29146            
29147            
29148                 
29149                 
29150                 
29151             default:
29152                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
29153                     
29154                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29155                     this.add(region, ret);
29156                 } else {
29157                 
29158                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
29159                     return null;
29160                 }
29161                 
29162              // GridPanel (grid, cfg)
29163             
29164         }
29165         this.beginUpdate();
29166         // add children..
29167         var region = '';
29168         var abn = {};
29169         Roo.each(xitems, function(i)  {
29170             region = nb && i.region ? i.region : false;
29171             
29172             var add = ret.addxtype(i);
29173            
29174             if (region) {
29175                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
29176                 if (!i.background) {
29177                     abn[region] = nb[region] ;
29178                 }
29179             }
29180             
29181         });
29182         this.endUpdate();
29183
29184         // make the last non-background panel active..
29185         //if (nb) { Roo.log(abn); }
29186         if (nb) {
29187             
29188             for(var r in abn) {
29189                 region = this.getRegion(r);
29190                 if (region) {
29191                     // tried using nb[r], but it does not work..
29192                      
29193                     region.showPanel(abn[r]);
29194                    
29195                 }
29196             }
29197         }
29198         return ret;
29199         
29200     }
29201 });
29202
29203 /**
29204  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
29205  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
29206  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
29207  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
29208  * <pre><code>
29209 // shorthand
29210 var CP = Roo.ContentPanel;
29211
29212 var layout = Roo.BorderLayout.create({
29213     north: {
29214         initialSize: 25,
29215         titlebar: false,
29216         panels: [new CP("north", "North")]
29217     },
29218     west: {
29219         split:true,
29220         initialSize: 200,
29221         minSize: 175,
29222         maxSize: 400,
29223         titlebar: true,
29224         collapsible: true,
29225         panels: [new CP("west", {title: "West"})]
29226     },
29227     east: {
29228         split:true,
29229         initialSize: 202,
29230         minSize: 175,
29231         maxSize: 400,
29232         titlebar: true,
29233         collapsible: true,
29234         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
29235     },
29236     south: {
29237         split:true,
29238         initialSize: 100,
29239         minSize: 100,
29240         maxSize: 200,
29241         titlebar: true,
29242         collapsible: true,
29243         panels: [new CP("south", {title: "South", closable: true})]
29244     },
29245     center: {
29246         titlebar: true,
29247         autoScroll:true,
29248         resizeTabs: true,
29249         minTabWidth: 50,
29250         preferredTabWidth: 150,
29251         panels: [
29252             new CP("center1", {title: "Close Me", closable: true}),
29253             new CP("center2", {title: "Center Panel", closable: false})
29254         ]
29255     }
29256 }, document.body);
29257
29258 layout.getRegion("center").showPanel("center1");
29259 </code></pre>
29260  * @param config
29261  * @param targetEl
29262  */
29263 Roo.BorderLayout.create = function(config, targetEl){
29264     var layout = new Roo.BorderLayout(targetEl || document.body, config);
29265     layout.beginUpdate();
29266     var regions = Roo.BorderLayout.RegionFactory.validRegions;
29267     for(var j = 0, jlen = regions.length; j < jlen; j++){
29268         var lr = regions[j];
29269         if(layout.regions[lr] && config[lr].panels){
29270             var r = layout.regions[lr];
29271             var ps = config[lr].panels;
29272             layout.addTypedPanels(r, ps);
29273         }
29274     }
29275     layout.endUpdate();
29276     return layout;
29277 };
29278
29279 // private
29280 Roo.BorderLayout.RegionFactory = {
29281     // private
29282     validRegions : ["north","south","east","west","center"],
29283
29284     // private
29285     create : function(target, mgr, config){
29286         target = target.toLowerCase();
29287         if(config.lightweight || config.basic){
29288             return new Roo.BasicLayoutRegion(mgr, config, target);
29289         }
29290         switch(target){
29291             case "north":
29292                 return new Roo.NorthLayoutRegion(mgr, config);
29293             case "south":
29294                 return new Roo.SouthLayoutRegion(mgr, config);
29295             case "east":
29296                 return new Roo.EastLayoutRegion(mgr, config);
29297             case "west":
29298                 return new Roo.WestLayoutRegion(mgr, config);
29299             case "center":
29300                 return new Roo.CenterLayoutRegion(mgr, config);
29301         }
29302         throw 'Layout region "'+target+'" not supported.';
29303     }
29304 };/*
29305  * Based on:
29306  * Ext JS Library 1.1.1
29307  * Copyright(c) 2006-2007, Ext JS, LLC.
29308  *
29309  * Originally Released Under LGPL - original licence link has changed is not relivant.
29310  *
29311  * Fork - LGPL
29312  * <script type="text/javascript">
29313  */
29314  
29315 /**
29316  * @class Roo.BasicLayoutRegion
29317  * @extends Roo.util.Observable
29318  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
29319  * and does not have a titlebar, tabs or any other features. All it does is size and position 
29320  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
29321  */
29322 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
29323     this.mgr = mgr;
29324     this.position  = pos;
29325     this.events = {
29326         /**
29327          * @scope Roo.BasicLayoutRegion
29328          */
29329         
29330         /**
29331          * @event beforeremove
29332          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
29333          * @param {Roo.LayoutRegion} this
29334          * @param {Roo.ContentPanel} panel The panel
29335          * @param {Object} e The cancel event object
29336          */
29337         "beforeremove" : true,
29338         /**
29339          * @event invalidated
29340          * Fires when the layout for this region is changed.
29341          * @param {Roo.LayoutRegion} this
29342          */
29343         "invalidated" : true,
29344         /**
29345          * @event visibilitychange
29346          * Fires when this region is shown or hidden 
29347          * @param {Roo.LayoutRegion} this
29348          * @param {Boolean} visibility true or false
29349          */
29350         "visibilitychange" : true,
29351         /**
29352          * @event paneladded
29353          * Fires when a panel is added. 
29354          * @param {Roo.LayoutRegion} this
29355          * @param {Roo.ContentPanel} panel The panel
29356          */
29357         "paneladded" : true,
29358         /**
29359          * @event panelremoved
29360          * Fires when a panel is removed. 
29361          * @param {Roo.LayoutRegion} this
29362          * @param {Roo.ContentPanel} panel The panel
29363          */
29364         "panelremoved" : true,
29365         /**
29366          * @event beforecollapse
29367          * Fires when this region before collapse.
29368          * @param {Roo.LayoutRegion} this
29369          */
29370         "beforecollapse" : true,
29371         /**
29372          * @event collapsed
29373          * Fires when this region is collapsed.
29374          * @param {Roo.LayoutRegion} this
29375          */
29376         "collapsed" : true,
29377         /**
29378          * @event expanded
29379          * Fires when this region is expanded.
29380          * @param {Roo.LayoutRegion} this
29381          */
29382         "expanded" : true,
29383         /**
29384          * @event slideshow
29385          * Fires when this region is slid into view.
29386          * @param {Roo.LayoutRegion} this
29387          */
29388         "slideshow" : true,
29389         /**
29390          * @event slidehide
29391          * Fires when this region slides out of view. 
29392          * @param {Roo.LayoutRegion} this
29393          */
29394         "slidehide" : true,
29395         /**
29396          * @event panelactivated
29397          * Fires when a panel is activated. 
29398          * @param {Roo.LayoutRegion} this
29399          * @param {Roo.ContentPanel} panel The activated panel
29400          */
29401         "panelactivated" : true,
29402         /**
29403          * @event resized
29404          * Fires when the user resizes this region. 
29405          * @param {Roo.LayoutRegion} this
29406          * @param {Number} newSize The new size (width for east/west, height for north/south)
29407          */
29408         "resized" : true
29409     };
29410     /** A collection of panels in this region. @type Roo.util.MixedCollection */
29411     this.panels = new Roo.util.MixedCollection();
29412     this.panels.getKey = this.getPanelId.createDelegate(this);
29413     this.box = null;
29414     this.activePanel = null;
29415     // ensure listeners are added...
29416     
29417     if (config.listeners || config.events) {
29418         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
29419             listeners : config.listeners || {},
29420             events : config.events || {}
29421         });
29422     }
29423     
29424     if(skipConfig !== true){
29425         this.applyConfig(config);
29426     }
29427 };
29428
29429 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
29430     getPanelId : function(p){
29431         return p.getId();
29432     },
29433     
29434     applyConfig : function(config){
29435         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29436         this.config = config;
29437         
29438     },
29439     
29440     /**
29441      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
29442      * the width, for horizontal (north, south) the height.
29443      * @param {Number} newSize The new width or height
29444      */
29445     resizeTo : function(newSize){
29446         var el = this.el ? this.el :
29447                  (this.activePanel ? this.activePanel.getEl() : null);
29448         if(el){
29449             switch(this.position){
29450                 case "east":
29451                 case "west":
29452                     el.setWidth(newSize);
29453                     this.fireEvent("resized", this, newSize);
29454                 break;
29455                 case "north":
29456                 case "south":
29457                     el.setHeight(newSize);
29458                     this.fireEvent("resized", this, newSize);
29459                 break;                
29460             }
29461         }
29462     },
29463     
29464     getBox : function(){
29465         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
29466     },
29467     
29468     getMargins : function(){
29469         return this.margins;
29470     },
29471     
29472     updateBox : function(box){
29473         this.box = box;
29474         var el = this.activePanel.getEl();
29475         el.dom.style.left = box.x + "px";
29476         el.dom.style.top = box.y + "px";
29477         this.activePanel.setSize(box.width, box.height);
29478     },
29479     
29480     /**
29481      * Returns the container element for this region.
29482      * @return {Roo.Element}
29483      */
29484     getEl : function(){
29485         return this.activePanel;
29486     },
29487     
29488     /**
29489      * Returns true if this region is currently visible.
29490      * @return {Boolean}
29491      */
29492     isVisible : function(){
29493         return this.activePanel ? true : false;
29494     },
29495     
29496     setActivePanel : function(panel){
29497         panel = this.getPanel(panel);
29498         if(this.activePanel && this.activePanel != panel){
29499             this.activePanel.setActiveState(false);
29500             this.activePanel.getEl().setLeftTop(-10000,-10000);
29501         }
29502         this.activePanel = panel;
29503         panel.setActiveState(true);
29504         if(this.box){
29505             panel.setSize(this.box.width, this.box.height);
29506         }
29507         this.fireEvent("panelactivated", this, panel);
29508         this.fireEvent("invalidated");
29509     },
29510     
29511     /**
29512      * Show the specified panel.
29513      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
29514      * @return {Roo.ContentPanel} The shown panel or null
29515      */
29516     showPanel : function(panel){
29517         if(panel = this.getPanel(panel)){
29518             this.setActivePanel(panel);
29519         }
29520         return panel;
29521     },
29522     
29523     /**
29524      * Get the active panel for this region.
29525      * @return {Roo.ContentPanel} The active panel or null
29526      */
29527     getActivePanel : function(){
29528         return this.activePanel;
29529     },
29530     
29531     /**
29532      * Add the passed ContentPanel(s)
29533      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
29534      * @return {Roo.ContentPanel} The panel added (if only one was added)
29535      */
29536     add : function(panel){
29537         if(arguments.length > 1){
29538             for(var i = 0, len = arguments.length; i < len; i++) {
29539                 this.add(arguments[i]);
29540             }
29541             return null;
29542         }
29543         if(this.hasPanel(panel)){
29544             this.showPanel(panel);
29545             return panel;
29546         }
29547         var el = panel.getEl();
29548         if(el.dom.parentNode != this.mgr.el.dom){
29549             this.mgr.el.dom.appendChild(el.dom);
29550         }
29551         if(panel.setRegion){
29552             panel.setRegion(this);
29553         }
29554         this.panels.add(panel);
29555         el.setStyle("position", "absolute");
29556         if(!panel.background){
29557             this.setActivePanel(panel);
29558             if(this.config.initialSize && this.panels.getCount()==1){
29559                 this.resizeTo(this.config.initialSize);
29560             }
29561         }
29562         this.fireEvent("paneladded", this, panel);
29563         return panel;
29564     },
29565     
29566     /**
29567      * Returns true if the panel is in this region.
29568      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29569      * @return {Boolean}
29570      */
29571     hasPanel : function(panel){
29572         if(typeof panel == "object"){ // must be panel obj
29573             panel = panel.getId();
29574         }
29575         return this.getPanel(panel) ? true : false;
29576     },
29577     
29578     /**
29579      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
29580      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29581      * @param {Boolean} preservePanel Overrides the config preservePanel option
29582      * @return {Roo.ContentPanel} The panel that was removed
29583      */
29584     remove : function(panel, preservePanel){
29585         panel = this.getPanel(panel);
29586         if(!panel){
29587             return null;
29588         }
29589         var e = {};
29590         this.fireEvent("beforeremove", this, panel, e);
29591         if(e.cancel === true){
29592             return null;
29593         }
29594         var panelId = panel.getId();
29595         this.panels.removeKey(panelId);
29596         return panel;
29597     },
29598     
29599     /**
29600      * Returns the panel specified or null if it's not in this region.
29601      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29602      * @return {Roo.ContentPanel}
29603      */
29604     getPanel : function(id){
29605         if(typeof id == "object"){ // must be panel obj
29606             return id;
29607         }
29608         return this.panels.get(id);
29609     },
29610     
29611     /**
29612      * Returns this regions position (north/south/east/west/center).
29613      * @return {String} 
29614      */
29615     getPosition: function(){
29616         return this.position;    
29617     }
29618 });/*
29619  * Based on:
29620  * Ext JS Library 1.1.1
29621  * Copyright(c) 2006-2007, Ext JS, LLC.
29622  *
29623  * Originally Released Under LGPL - original licence link has changed is not relivant.
29624  *
29625  * Fork - LGPL
29626  * <script type="text/javascript">
29627  */
29628  
29629 /**
29630  * @class Roo.LayoutRegion
29631  * @extends Roo.BasicLayoutRegion
29632  * This class represents a region in a layout manager.
29633  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
29634  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
29635  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
29636  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
29637  * @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})
29638  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
29639  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
29640  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
29641  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
29642  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
29643  * @cfg {String}    title           The title for the region (overrides panel titles)
29644  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
29645  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
29646  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
29647  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
29648  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
29649  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
29650  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
29651  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
29652  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
29653  * @cfg {Boolean}   showPin         True to show a pin button
29654  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
29655  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
29656  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
29657  * @cfg {Number}    width           For East/West panels
29658  * @cfg {Number}    height          For North/South panels
29659  * @cfg {Boolean}   split           To show the splitter
29660  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
29661  */
29662 Roo.LayoutRegion = function(mgr, config, pos){
29663     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
29664     var dh = Roo.DomHelper;
29665     /** This region's container element 
29666     * @type Roo.Element */
29667     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
29668     /** This region's title element 
29669     * @type Roo.Element */
29670
29671     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
29672         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
29673         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
29674     ]}, true);
29675     this.titleEl.enableDisplayMode();
29676     /** This region's title text element 
29677     * @type HTMLElement */
29678     this.titleTextEl = this.titleEl.dom.firstChild;
29679     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
29680     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
29681     this.closeBtn.enableDisplayMode();
29682     this.closeBtn.on("click", this.closeClicked, this);
29683     this.closeBtn.hide();
29684
29685     this.createBody(config);
29686     this.visible = true;
29687     this.collapsed = false;
29688
29689     if(config.hideWhenEmpty){
29690         this.hide();
29691         this.on("paneladded", this.validateVisibility, this);
29692         this.on("panelremoved", this.validateVisibility, this);
29693     }
29694     this.applyConfig(config);
29695 };
29696
29697 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
29698
29699     createBody : function(){
29700         /** This region's body element 
29701         * @type Roo.Element */
29702         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
29703     },
29704
29705     applyConfig : function(c){
29706         if(c.collapsible && this.position != "center" && !this.collapsedEl){
29707             var dh = Roo.DomHelper;
29708             if(c.titlebar !== false){
29709                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
29710                 this.collapseBtn.on("click", this.collapse, this);
29711                 this.collapseBtn.enableDisplayMode();
29712
29713                 if(c.showPin === true || this.showPin){
29714                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
29715                     this.stickBtn.enableDisplayMode();
29716                     this.stickBtn.on("click", this.expand, this);
29717                     this.stickBtn.hide();
29718                 }
29719             }
29720             /** This region's collapsed element
29721             * @type Roo.Element */
29722             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
29723                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
29724             ]}, true);
29725             if(c.floatable !== false){
29726                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
29727                this.collapsedEl.on("click", this.collapseClick, this);
29728             }
29729
29730             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
29731                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
29732                    id: "message", unselectable: "on", style:{"float":"left"}});
29733                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
29734              }
29735             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
29736             this.expandBtn.on("click", this.expand, this);
29737         }
29738         if(this.collapseBtn){
29739             this.collapseBtn.setVisible(c.collapsible == true);
29740         }
29741         this.cmargins = c.cmargins || this.cmargins ||
29742                          (this.position == "west" || this.position == "east" ?
29743                              {top: 0, left: 2, right:2, bottom: 0} :
29744                              {top: 2, left: 0, right:0, bottom: 2});
29745         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29746         this.bottomTabs = c.tabPosition != "top";
29747         this.autoScroll = c.autoScroll || false;
29748         if(this.autoScroll){
29749             this.bodyEl.setStyle("overflow", "auto");
29750         }else{
29751             this.bodyEl.setStyle("overflow", "hidden");
29752         }
29753         //if(c.titlebar !== false){
29754             if((!c.titlebar && !c.title) || c.titlebar === false){
29755                 this.titleEl.hide();
29756             }else{
29757                 this.titleEl.show();
29758                 if(c.title){
29759                     this.titleTextEl.innerHTML = c.title;
29760                 }
29761             }
29762         //}
29763         this.duration = c.duration || .30;
29764         this.slideDuration = c.slideDuration || .45;
29765         this.config = c;
29766         if(c.collapsed){
29767             this.collapse(true);
29768         }
29769         if(c.hidden){
29770             this.hide();
29771         }
29772     },
29773     /**
29774      * Returns true if this region is currently visible.
29775      * @return {Boolean}
29776      */
29777     isVisible : function(){
29778         return this.visible;
29779     },
29780
29781     /**
29782      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
29783      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
29784      */
29785     setCollapsedTitle : function(title){
29786         title = title || "&#160;";
29787         if(this.collapsedTitleTextEl){
29788             this.collapsedTitleTextEl.innerHTML = title;
29789         }
29790     },
29791
29792     getBox : function(){
29793         var b;
29794         if(!this.collapsed){
29795             b = this.el.getBox(false, true);
29796         }else{
29797             b = this.collapsedEl.getBox(false, true);
29798         }
29799         return b;
29800     },
29801
29802     getMargins : function(){
29803         return this.collapsed ? this.cmargins : this.margins;
29804     },
29805
29806     highlight : function(){
29807         this.el.addClass("x-layout-panel-dragover");
29808     },
29809
29810     unhighlight : function(){
29811         this.el.removeClass("x-layout-panel-dragover");
29812     },
29813
29814     updateBox : function(box){
29815         this.box = box;
29816         if(!this.collapsed){
29817             this.el.dom.style.left = box.x + "px";
29818             this.el.dom.style.top = box.y + "px";
29819             this.updateBody(box.width, box.height);
29820         }else{
29821             this.collapsedEl.dom.style.left = box.x + "px";
29822             this.collapsedEl.dom.style.top = box.y + "px";
29823             this.collapsedEl.setSize(box.width, box.height);
29824         }
29825         if(this.tabs){
29826             this.tabs.autoSizeTabs();
29827         }
29828     },
29829
29830     updateBody : function(w, h){
29831         if(w !== null){
29832             this.el.setWidth(w);
29833             w -= this.el.getBorderWidth("rl");
29834             if(this.config.adjustments){
29835                 w += this.config.adjustments[0];
29836             }
29837         }
29838         if(h !== null){
29839             this.el.setHeight(h);
29840             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
29841             h -= this.el.getBorderWidth("tb");
29842             if(this.config.adjustments){
29843                 h += this.config.adjustments[1];
29844             }
29845             this.bodyEl.setHeight(h);
29846             if(this.tabs){
29847                 h = this.tabs.syncHeight(h);
29848             }
29849         }
29850         if(this.panelSize){
29851             w = w !== null ? w : this.panelSize.width;
29852             h = h !== null ? h : this.panelSize.height;
29853         }
29854         if(this.activePanel){
29855             var el = this.activePanel.getEl();
29856             w = w !== null ? w : el.getWidth();
29857             h = h !== null ? h : el.getHeight();
29858             this.panelSize = {width: w, height: h};
29859             this.activePanel.setSize(w, h);
29860         }
29861         if(Roo.isIE && this.tabs){
29862             this.tabs.el.repaint();
29863         }
29864     },
29865
29866     /**
29867      * Returns the container element for this region.
29868      * @return {Roo.Element}
29869      */
29870     getEl : function(){
29871         return this.el;
29872     },
29873
29874     /**
29875      * Hides this region.
29876      */
29877     hide : function(){
29878         if(!this.collapsed){
29879             this.el.dom.style.left = "-2000px";
29880             this.el.hide();
29881         }else{
29882             this.collapsedEl.dom.style.left = "-2000px";
29883             this.collapsedEl.hide();
29884         }
29885         this.visible = false;
29886         this.fireEvent("visibilitychange", this, false);
29887     },
29888
29889     /**
29890      * Shows this region if it was previously hidden.
29891      */
29892     show : function(){
29893         if(!this.collapsed){
29894             this.el.show();
29895         }else{
29896             this.collapsedEl.show();
29897         }
29898         this.visible = true;
29899         this.fireEvent("visibilitychange", this, true);
29900     },
29901
29902     closeClicked : function(){
29903         if(this.activePanel){
29904             this.remove(this.activePanel);
29905         }
29906     },
29907
29908     collapseClick : function(e){
29909         if(this.isSlid){
29910            e.stopPropagation();
29911            this.slideIn();
29912         }else{
29913            e.stopPropagation();
29914            this.slideOut();
29915         }
29916     },
29917
29918     /**
29919      * Collapses this region.
29920      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
29921      */
29922     collapse : function(skipAnim, skipCheck = false){
29923         if(this.collapsed) {
29924             return;
29925         }
29926         
29927         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
29928             
29929             this.collapsed = true;
29930             if(this.split){
29931                 this.split.el.hide();
29932             }
29933             if(this.config.animate && skipAnim !== true){
29934                 this.fireEvent("invalidated", this);
29935                 this.animateCollapse();
29936             }else{
29937                 this.el.setLocation(-20000,-20000);
29938                 this.el.hide();
29939                 this.collapsedEl.show();
29940                 this.fireEvent("collapsed", this);
29941                 this.fireEvent("invalidated", this);
29942             }
29943         }
29944         
29945     },
29946
29947     animateCollapse : function(){
29948         // overridden
29949     },
29950
29951     /**
29952      * Expands this region if it was previously collapsed.
29953      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
29954      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
29955      */
29956     expand : function(e, skipAnim){
29957         if(e) {
29958             e.stopPropagation();
29959         }
29960         if(!this.collapsed || this.el.hasActiveFx()) {
29961             return;
29962         }
29963         if(this.isSlid){
29964             this.afterSlideIn();
29965             skipAnim = true;
29966         }
29967         this.collapsed = false;
29968         if(this.config.animate && skipAnim !== true){
29969             this.animateExpand();
29970         }else{
29971             this.el.show();
29972             if(this.split){
29973                 this.split.el.show();
29974             }
29975             this.collapsedEl.setLocation(-2000,-2000);
29976             this.collapsedEl.hide();
29977             this.fireEvent("invalidated", this);
29978             this.fireEvent("expanded", this);
29979         }
29980     },
29981
29982     animateExpand : function(){
29983         // overridden
29984     },
29985
29986     initTabs : function()
29987     {
29988         this.bodyEl.setStyle("overflow", "hidden");
29989         var ts = new Roo.TabPanel(
29990                 this.bodyEl.dom,
29991                 {
29992                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
29993                     disableTooltips: this.config.disableTabTips,
29994                     toolbar : this.config.toolbar
29995                 }
29996         );
29997         if(this.config.hideTabs){
29998             ts.stripWrap.setDisplayed(false);
29999         }
30000         this.tabs = ts;
30001         ts.resizeTabs = this.config.resizeTabs === true;
30002         ts.minTabWidth = this.config.minTabWidth || 40;
30003         ts.maxTabWidth = this.config.maxTabWidth || 250;
30004         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
30005         ts.monitorResize = false;
30006         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30007         ts.bodyEl.addClass('x-layout-tabs-body');
30008         this.panels.each(this.initPanelAsTab, this);
30009     },
30010
30011     initPanelAsTab : function(panel){
30012         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
30013                     this.config.closeOnTab && panel.isClosable());
30014         if(panel.tabTip !== undefined){
30015             ti.setTooltip(panel.tabTip);
30016         }
30017         ti.on("activate", function(){
30018               this.setActivePanel(panel);
30019         }, this);
30020         if(this.config.closeOnTab){
30021             ti.on("beforeclose", function(t, e){
30022                 e.cancel = true;
30023                 this.remove(panel);
30024             }, this);
30025         }
30026         return ti;
30027     },
30028
30029     updatePanelTitle : function(panel, title){
30030         if(this.activePanel == panel){
30031             this.updateTitle(title);
30032         }
30033         if(this.tabs){
30034             var ti = this.tabs.getTab(panel.getEl().id);
30035             ti.setText(title);
30036             if(panel.tabTip !== undefined){
30037                 ti.setTooltip(panel.tabTip);
30038             }
30039         }
30040     },
30041
30042     updateTitle : function(title){
30043         if(this.titleTextEl && !this.config.title){
30044             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
30045         }
30046     },
30047
30048     setActivePanel : function(panel){
30049         panel = this.getPanel(panel);
30050         if(this.activePanel && this.activePanel != panel){
30051             this.activePanel.setActiveState(false);
30052         }
30053         this.activePanel = panel;
30054         panel.setActiveState(true);
30055         if(this.panelSize){
30056             panel.setSize(this.panelSize.width, this.panelSize.height);
30057         }
30058         if(this.closeBtn){
30059             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
30060         }
30061         this.updateTitle(panel.getTitle());
30062         if(this.tabs){
30063             this.fireEvent("invalidated", this);
30064         }
30065         this.fireEvent("panelactivated", this, panel);
30066     },
30067
30068     /**
30069      * Shows the specified panel.
30070      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
30071      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
30072      */
30073     showPanel : function(panel)
30074     {
30075         panel = this.getPanel(panel);
30076         if(panel){
30077             if(this.tabs){
30078                 var tab = this.tabs.getTab(panel.getEl().id);
30079                 if(tab.isHidden()){
30080                     this.tabs.unhideTab(tab.id);
30081                 }
30082                 tab.activate();
30083             }else{
30084                 this.setActivePanel(panel);
30085             }
30086         }
30087         return panel;
30088     },
30089
30090     /**
30091      * Get the active panel for this region.
30092      * @return {Roo.ContentPanel} The active panel or null
30093      */
30094     getActivePanel : function(){
30095         return this.activePanel;
30096     },
30097
30098     validateVisibility : function(){
30099         if(this.panels.getCount() < 1){
30100             this.updateTitle("&#160;");
30101             this.closeBtn.hide();
30102             this.hide();
30103         }else{
30104             if(!this.isVisible()){
30105                 this.show();
30106             }
30107         }
30108     },
30109
30110     /**
30111      * Adds the passed ContentPanel(s) to this region.
30112      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
30113      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
30114      */
30115     add : function(panel){
30116         if(arguments.length > 1){
30117             for(var i = 0, len = arguments.length; i < len; i++) {
30118                 this.add(arguments[i]);
30119             }
30120             return null;
30121         }
30122         if(this.hasPanel(panel)){
30123             this.showPanel(panel);
30124             return panel;
30125         }
30126         panel.setRegion(this);
30127         this.panels.add(panel);
30128         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
30129             this.bodyEl.dom.appendChild(panel.getEl().dom);
30130             if(panel.background !== true){
30131                 this.setActivePanel(panel);
30132             }
30133             this.fireEvent("paneladded", this, panel);
30134             return panel;
30135         }
30136         if(!this.tabs){
30137             this.initTabs();
30138         }else{
30139             this.initPanelAsTab(panel);
30140         }
30141         if(panel.background !== true){
30142             this.tabs.activate(panel.getEl().id);
30143         }
30144         this.fireEvent("paneladded", this, panel);
30145         return panel;
30146     },
30147
30148     /**
30149      * Hides the tab for the specified panel.
30150      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30151      */
30152     hidePanel : function(panel){
30153         if(this.tabs && (panel = this.getPanel(panel))){
30154             this.tabs.hideTab(panel.getEl().id);
30155         }
30156     },
30157
30158     /**
30159      * Unhides the tab for a previously hidden panel.
30160      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30161      */
30162     unhidePanel : function(panel){
30163         if(this.tabs && (panel = this.getPanel(panel))){
30164             this.tabs.unhideTab(panel.getEl().id);
30165         }
30166     },
30167
30168     clearPanels : function(){
30169         while(this.panels.getCount() > 0){
30170              this.remove(this.panels.first());
30171         }
30172     },
30173
30174     /**
30175      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30176      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30177      * @param {Boolean} preservePanel Overrides the config preservePanel option
30178      * @return {Roo.ContentPanel} The panel that was removed
30179      */
30180     remove : function(panel, preservePanel){
30181         panel = this.getPanel(panel);
30182         if(!panel){
30183             return null;
30184         }
30185         var e = {};
30186         this.fireEvent("beforeremove", this, panel, e);
30187         if(e.cancel === true){
30188             return null;
30189         }
30190         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
30191         var panelId = panel.getId();
30192         this.panels.removeKey(panelId);
30193         if(preservePanel){
30194             document.body.appendChild(panel.getEl().dom);
30195         }
30196         if(this.tabs){
30197             this.tabs.removeTab(panel.getEl().id);
30198         }else if (!preservePanel){
30199             this.bodyEl.dom.removeChild(panel.getEl().dom);
30200         }
30201         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
30202             var p = this.panels.first();
30203             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
30204             tempEl.appendChild(p.getEl().dom);
30205             this.bodyEl.update("");
30206             this.bodyEl.dom.appendChild(p.getEl().dom);
30207             tempEl = null;
30208             this.updateTitle(p.getTitle());
30209             this.tabs = null;
30210             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30211             this.setActivePanel(p);
30212         }
30213         panel.setRegion(null);
30214         if(this.activePanel == panel){
30215             this.activePanel = null;
30216         }
30217         if(this.config.autoDestroy !== false && preservePanel !== true){
30218             try{panel.destroy();}catch(e){}
30219         }
30220         this.fireEvent("panelremoved", this, panel);
30221         return panel;
30222     },
30223
30224     /**
30225      * Returns the TabPanel component used by this region
30226      * @return {Roo.TabPanel}
30227      */
30228     getTabs : function(){
30229         return this.tabs;
30230     },
30231
30232     createTool : function(parentEl, className){
30233         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
30234             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
30235         btn.addClassOnOver("x-layout-tools-button-over");
30236         return btn;
30237     }
30238 });/*
30239  * Based on:
30240  * Ext JS Library 1.1.1
30241  * Copyright(c) 2006-2007, Ext JS, LLC.
30242  *
30243  * Originally Released Under LGPL - original licence link has changed is not relivant.
30244  *
30245  * Fork - LGPL
30246  * <script type="text/javascript">
30247  */
30248  
30249
30250
30251 /**
30252  * @class Roo.SplitLayoutRegion
30253  * @extends Roo.LayoutRegion
30254  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
30255  */
30256 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
30257     this.cursor = cursor;
30258     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
30259 };
30260
30261 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
30262     splitTip : "Drag to resize.",
30263     collapsibleSplitTip : "Drag to resize. Double click to hide.",
30264     useSplitTips : false,
30265
30266     applyConfig : function(config){
30267         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
30268         if(config.split){
30269             if(!this.split){
30270                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
30271                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
30272                 /** The SplitBar for this region 
30273                 * @type Roo.SplitBar */
30274                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
30275                 this.split.on("moved", this.onSplitMove, this);
30276                 this.split.useShim = config.useShim === true;
30277                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
30278                 if(this.useSplitTips){
30279                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
30280                 }
30281                 if(config.collapsible){
30282                     this.split.el.on("dblclick", this.collapse,  this);
30283                 }
30284             }
30285             if(typeof config.minSize != "undefined"){
30286                 this.split.minSize = config.minSize;
30287             }
30288             if(typeof config.maxSize != "undefined"){
30289                 this.split.maxSize = config.maxSize;
30290             }
30291             if(config.hideWhenEmpty || config.hidden || config.collapsed){
30292                 this.hideSplitter();
30293             }
30294         }
30295     },
30296
30297     getHMaxSize : function(){
30298          var cmax = this.config.maxSize || 10000;
30299          var center = this.mgr.getRegion("center");
30300          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
30301     },
30302
30303     getVMaxSize : function(){
30304          var cmax = this.config.maxSize || 10000;
30305          var center = this.mgr.getRegion("center");
30306          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
30307     },
30308
30309     onSplitMove : function(split, newSize){
30310         this.fireEvent("resized", this, newSize);
30311     },
30312     
30313     /** 
30314      * Returns the {@link Roo.SplitBar} for this region.
30315      * @return {Roo.SplitBar}
30316      */
30317     getSplitBar : function(){
30318         return this.split;
30319     },
30320     
30321     hide : function(){
30322         this.hideSplitter();
30323         Roo.SplitLayoutRegion.superclass.hide.call(this);
30324     },
30325
30326     hideSplitter : function(){
30327         if(this.split){
30328             this.split.el.setLocation(-2000,-2000);
30329             this.split.el.hide();
30330         }
30331     },
30332
30333     show : function(){
30334         if(this.split){
30335             this.split.el.show();
30336         }
30337         Roo.SplitLayoutRegion.superclass.show.call(this);
30338     },
30339     
30340     beforeSlide: function(){
30341         if(Roo.isGecko){// firefox overflow auto bug workaround
30342             this.bodyEl.clip();
30343             if(this.tabs) {
30344                 this.tabs.bodyEl.clip();
30345             }
30346             if(this.activePanel){
30347                 this.activePanel.getEl().clip();
30348                 
30349                 if(this.activePanel.beforeSlide){
30350                     this.activePanel.beforeSlide();
30351                 }
30352             }
30353         }
30354     },
30355     
30356     afterSlide : function(){
30357         if(Roo.isGecko){// firefox overflow auto bug workaround
30358             this.bodyEl.unclip();
30359             if(this.tabs) {
30360                 this.tabs.bodyEl.unclip();
30361             }
30362             if(this.activePanel){
30363                 this.activePanel.getEl().unclip();
30364                 if(this.activePanel.afterSlide){
30365                     this.activePanel.afterSlide();
30366                 }
30367             }
30368         }
30369     },
30370
30371     initAutoHide : function(){
30372         if(this.autoHide !== false){
30373             if(!this.autoHideHd){
30374                 var st = new Roo.util.DelayedTask(this.slideIn, this);
30375                 this.autoHideHd = {
30376                     "mouseout": function(e){
30377                         if(!e.within(this.el, true)){
30378                             st.delay(500);
30379                         }
30380                     },
30381                     "mouseover" : function(e){
30382                         st.cancel();
30383                     },
30384                     scope : this
30385                 };
30386             }
30387             this.el.on(this.autoHideHd);
30388         }
30389     },
30390
30391     clearAutoHide : function(){
30392         if(this.autoHide !== false){
30393             this.el.un("mouseout", this.autoHideHd.mouseout);
30394             this.el.un("mouseover", this.autoHideHd.mouseover);
30395         }
30396     },
30397
30398     clearMonitor : function(){
30399         Roo.get(document).un("click", this.slideInIf, this);
30400     },
30401
30402     // these names are backwards but not changed for compat
30403     slideOut : function(){
30404         if(this.isSlid || this.el.hasActiveFx()){
30405             return;
30406         }
30407         this.isSlid = true;
30408         if(this.collapseBtn){
30409             this.collapseBtn.hide();
30410         }
30411         this.closeBtnState = this.closeBtn.getStyle('display');
30412         this.closeBtn.hide();
30413         if(this.stickBtn){
30414             this.stickBtn.show();
30415         }
30416         this.el.show();
30417         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
30418         this.beforeSlide();
30419         this.el.setStyle("z-index", 10001);
30420         this.el.slideIn(this.getSlideAnchor(), {
30421             callback: function(){
30422                 this.afterSlide();
30423                 this.initAutoHide();
30424                 Roo.get(document).on("click", this.slideInIf, this);
30425                 this.fireEvent("slideshow", this);
30426             },
30427             scope: this,
30428             block: true
30429         });
30430     },
30431
30432     afterSlideIn : function(){
30433         this.clearAutoHide();
30434         this.isSlid = false;
30435         this.clearMonitor();
30436         this.el.setStyle("z-index", "");
30437         if(this.collapseBtn){
30438             this.collapseBtn.show();
30439         }
30440         this.closeBtn.setStyle('display', this.closeBtnState);
30441         if(this.stickBtn){
30442             this.stickBtn.hide();
30443         }
30444         this.fireEvent("slidehide", this);
30445     },
30446
30447     slideIn : function(cb){
30448         if(!this.isSlid || this.el.hasActiveFx()){
30449             Roo.callback(cb);
30450             return;
30451         }
30452         this.isSlid = false;
30453         this.beforeSlide();
30454         this.el.slideOut(this.getSlideAnchor(), {
30455             callback: function(){
30456                 this.el.setLeftTop(-10000, -10000);
30457                 this.afterSlide();
30458                 this.afterSlideIn();
30459                 Roo.callback(cb);
30460             },
30461             scope: this,
30462             block: true
30463         });
30464     },
30465     
30466     slideInIf : function(e){
30467         if(!e.within(this.el)){
30468             this.slideIn();
30469         }
30470     },
30471
30472     animateCollapse : function(){
30473         this.beforeSlide();
30474         this.el.setStyle("z-index", 20000);
30475         var anchor = this.getSlideAnchor();
30476         this.el.slideOut(anchor, {
30477             callback : function(){
30478                 this.el.setStyle("z-index", "");
30479                 this.collapsedEl.slideIn(anchor, {duration:.3});
30480                 this.afterSlide();
30481                 this.el.setLocation(-10000,-10000);
30482                 this.el.hide();
30483                 this.fireEvent("collapsed", this);
30484             },
30485             scope: this,
30486             block: true
30487         });
30488     },
30489
30490     animateExpand : function(){
30491         this.beforeSlide();
30492         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
30493         this.el.setStyle("z-index", 20000);
30494         this.collapsedEl.hide({
30495             duration:.1
30496         });
30497         this.el.slideIn(this.getSlideAnchor(), {
30498             callback : function(){
30499                 this.el.setStyle("z-index", "");
30500                 this.afterSlide();
30501                 if(this.split){
30502                     this.split.el.show();
30503                 }
30504                 this.fireEvent("invalidated", this);
30505                 this.fireEvent("expanded", this);
30506             },
30507             scope: this,
30508             block: true
30509         });
30510     },
30511
30512     anchors : {
30513         "west" : "left",
30514         "east" : "right",
30515         "north" : "top",
30516         "south" : "bottom"
30517     },
30518
30519     sanchors : {
30520         "west" : "l",
30521         "east" : "r",
30522         "north" : "t",
30523         "south" : "b"
30524     },
30525
30526     canchors : {
30527         "west" : "tl-tr",
30528         "east" : "tr-tl",
30529         "north" : "tl-bl",
30530         "south" : "bl-tl"
30531     },
30532
30533     getAnchor : function(){
30534         return this.anchors[this.position];
30535     },
30536
30537     getCollapseAnchor : function(){
30538         return this.canchors[this.position];
30539     },
30540
30541     getSlideAnchor : function(){
30542         return this.sanchors[this.position];
30543     },
30544
30545     getAlignAdj : function(){
30546         var cm = this.cmargins;
30547         switch(this.position){
30548             case "west":
30549                 return [0, 0];
30550             break;
30551             case "east":
30552                 return [0, 0];
30553             break;
30554             case "north":
30555                 return [0, 0];
30556             break;
30557             case "south":
30558                 return [0, 0];
30559             break;
30560         }
30561     },
30562
30563     getExpandAdj : function(){
30564         var c = this.collapsedEl, cm = this.cmargins;
30565         switch(this.position){
30566             case "west":
30567                 return [-(cm.right+c.getWidth()+cm.left), 0];
30568             break;
30569             case "east":
30570                 return [cm.right+c.getWidth()+cm.left, 0];
30571             break;
30572             case "north":
30573                 return [0, -(cm.top+cm.bottom+c.getHeight())];
30574             break;
30575             case "south":
30576                 return [0, cm.top+cm.bottom+c.getHeight()];
30577             break;
30578         }
30579     }
30580 });/*
30581  * Based on:
30582  * Ext JS Library 1.1.1
30583  * Copyright(c) 2006-2007, Ext JS, LLC.
30584  *
30585  * Originally Released Under LGPL - original licence link has changed is not relivant.
30586  *
30587  * Fork - LGPL
30588  * <script type="text/javascript">
30589  */
30590 /*
30591  * These classes are private internal classes
30592  */
30593 Roo.CenterLayoutRegion = function(mgr, config){
30594     Roo.LayoutRegion.call(this, mgr, config, "center");
30595     this.visible = true;
30596     this.minWidth = config.minWidth || 20;
30597     this.minHeight = config.minHeight || 20;
30598 };
30599
30600 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
30601     hide : function(){
30602         // center panel can't be hidden
30603     },
30604     
30605     show : function(){
30606         // center panel can't be hidden
30607     },
30608     
30609     getMinWidth: function(){
30610         return this.minWidth;
30611     },
30612     
30613     getMinHeight: function(){
30614         return this.minHeight;
30615     }
30616 });
30617
30618
30619 Roo.NorthLayoutRegion = function(mgr, config){
30620     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
30621     if(this.split){
30622         this.split.placement = Roo.SplitBar.TOP;
30623         this.split.orientation = Roo.SplitBar.VERTICAL;
30624         this.split.el.addClass("x-layout-split-v");
30625     }
30626     var size = config.initialSize || config.height;
30627     if(typeof size != "undefined"){
30628         this.el.setHeight(size);
30629     }
30630 };
30631 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
30632     orientation: Roo.SplitBar.VERTICAL,
30633     getBox : function(){
30634         if(this.collapsed){
30635             return this.collapsedEl.getBox();
30636         }
30637         var box = this.el.getBox();
30638         if(this.split){
30639             box.height += this.split.el.getHeight();
30640         }
30641         return box;
30642     },
30643     
30644     updateBox : function(box){
30645         if(this.split && !this.collapsed){
30646             box.height -= this.split.el.getHeight();
30647             this.split.el.setLeft(box.x);
30648             this.split.el.setTop(box.y+box.height);
30649             this.split.el.setWidth(box.width);
30650         }
30651         if(this.collapsed){
30652             this.updateBody(box.width, null);
30653         }
30654         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30655     }
30656 });
30657
30658 Roo.SouthLayoutRegion = function(mgr, config){
30659     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
30660     if(this.split){
30661         this.split.placement = Roo.SplitBar.BOTTOM;
30662         this.split.orientation = Roo.SplitBar.VERTICAL;
30663         this.split.el.addClass("x-layout-split-v");
30664     }
30665     var size = config.initialSize || config.height;
30666     if(typeof size != "undefined"){
30667         this.el.setHeight(size);
30668     }
30669 };
30670 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
30671     orientation: Roo.SplitBar.VERTICAL,
30672     getBox : function(){
30673         if(this.collapsed){
30674             return this.collapsedEl.getBox();
30675         }
30676         var box = this.el.getBox();
30677         if(this.split){
30678             var sh = this.split.el.getHeight();
30679             box.height += sh;
30680             box.y -= sh;
30681         }
30682         return box;
30683     },
30684     
30685     updateBox : function(box){
30686         if(this.split && !this.collapsed){
30687             var sh = this.split.el.getHeight();
30688             box.height -= sh;
30689             box.y += sh;
30690             this.split.el.setLeft(box.x);
30691             this.split.el.setTop(box.y-sh);
30692             this.split.el.setWidth(box.width);
30693         }
30694         if(this.collapsed){
30695             this.updateBody(box.width, null);
30696         }
30697         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30698     }
30699 });
30700
30701 Roo.EastLayoutRegion = function(mgr, config){
30702     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
30703     if(this.split){
30704         this.split.placement = Roo.SplitBar.RIGHT;
30705         this.split.orientation = Roo.SplitBar.HORIZONTAL;
30706         this.split.el.addClass("x-layout-split-h");
30707     }
30708     var size = config.initialSize || config.width;
30709     if(typeof size != "undefined"){
30710         this.el.setWidth(size);
30711     }
30712 };
30713 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
30714     orientation: Roo.SplitBar.HORIZONTAL,
30715     getBox : function(){
30716         if(this.collapsed){
30717             return this.collapsedEl.getBox();
30718         }
30719         var box = this.el.getBox();
30720         if(this.split){
30721             var sw = this.split.el.getWidth();
30722             box.width += sw;
30723             box.x -= sw;
30724         }
30725         return box;
30726     },
30727
30728     updateBox : function(box){
30729         if(this.split && !this.collapsed){
30730             var sw = this.split.el.getWidth();
30731             box.width -= sw;
30732             this.split.el.setLeft(box.x);
30733             this.split.el.setTop(box.y);
30734             this.split.el.setHeight(box.height);
30735             box.x += sw;
30736         }
30737         if(this.collapsed){
30738             this.updateBody(null, box.height);
30739         }
30740         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30741     }
30742 });
30743
30744 Roo.WestLayoutRegion = function(mgr, config){
30745     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
30746     if(this.split){
30747         this.split.placement = Roo.SplitBar.LEFT;
30748         this.split.orientation = Roo.SplitBar.HORIZONTAL;
30749         this.split.el.addClass("x-layout-split-h");
30750     }
30751     var size = config.initialSize || config.width;
30752     if(typeof size != "undefined"){
30753         this.el.setWidth(size);
30754     }
30755 };
30756 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
30757     orientation: Roo.SplitBar.HORIZONTAL,
30758     getBox : function(){
30759         if(this.collapsed){
30760             return this.collapsedEl.getBox();
30761         }
30762         var box = this.el.getBox();
30763         if(this.split){
30764             box.width += this.split.el.getWidth();
30765         }
30766         return box;
30767     },
30768     
30769     updateBox : function(box){
30770         if(this.split && !this.collapsed){
30771             var sw = this.split.el.getWidth();
30772             box.width -= sw;
30773             this.split.el.setLeft(box.x+box.width);
30774             this.split.el.setTop(box.y);
30775             this.split.el.setHeight(box.height);
30776         }
30777         if(this.collapsed){
30778             this.updateBody(null, box.height);
30779         }
30780         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30781     }
30782 });
30783 /*
30784  * Based on:
30785  * Ext JS Library 1.1.1
30786  * Copyright(c) 2006-2007, Ext JS, LLC.
30787  *
30788  * Originally Released Under LGPL - original licence link has changed is not relivant.
30789  *
30790  * Fork - LGPL
30791  * <script type="text/javascript">
30792  */
30793  
30794  
30795 /*
30796  * Private internal class for reading and applying state
30797  */
30798 Roo.LayoutStateManager = function(layout){
30799      // default empty state
30800      this.state = {
30801         north: {},
30802         south: {},
30803         east: {},
30804         west: {}       
30805     };
30806 };
30807
30808 Roo.LayoutStateManager.prototype = {
30809     init : function(layout, provider){
30810         this.provider = provider;
30811         var state = provider.get(layout.id+"-layout-state");
30812         if(state){
30813             var wasUpdating = layout.isUpdating();
30814             if(!wasUpdating){
30815                 layout.beginUpdate();
30816             }
30817             for(var key in state){
30818                 if(typeof state[key] != "function"){
30819                     var rstate = state[key];
30820                     var r = layout.getRegion(key);
30821                     if(r && rstate){
30822                         if(rstate.size){
30823                             r.resizeTo(rstate.size);
30824                         }
30825                         if(rstate.collapsed == true){
30826                             r.collapse(true);
30827                         }else{
30828                             r.expand(null, true);
30829                         }
30830                     }
30831                 }
30832             }
30833             if(!wasUpdating){
30834                 layout.endUpdate();
30835             }
30836             this.state = state; 
30837         }
30838         this.layout = layout;
30839         layout.on("regionresized", this.onRegionResized, this);
30840         layout.on("regioncollapsed", this.onRegionCollapsed, this);
30841         layout.on("regionexpanded", this.onRegionExpanded, this);
30842     },
30843     
30844     storeState : function(){
30845         this.provider.set(this.layout.id+"-layout-state", this.state);
30846     },
30847     
30848     onRegionResized : function(region, newSize){
30849         this.state[region.getPosition()].size = newSize;
30850         this.storeState();
30851     },
30852     
30853     onRegionCollapsed : function(region){
30854         this.state[region.getPosition()].collapsed = true;
30855         this.storeState();
30856     },
30857     
30858     onRegionExpanded : function(region){
30859         this.state[region.getPosition()].collapsed = false;
30860         this.storeState();
30861     }
30862 };/*
30863  * Based on:
30864  * Ext JS Library 1.1.1
30865  * Copyright(c) 2006-2007, Ext JS, LLC.
30866  *
30867  * Originally Released Under LGPL - original licence link has changed is not relivant.
30868  *
30869  * Fork - LGPL
30870  * <script type="text/javascript">
30871  */
30872 /**
30873  * @class Roo.ContentPanel
30874  * @extends Roo.util.Observable
30875  * A basic ContentPanel element.
30876  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
30877  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
30878  * @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
30879  * @cfg {Boolean}   closable      True if the panel can be closed/removed
30880  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
30881  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
30882  * @cfg {Toolbar}   toolbar       A toolbar for this panel
30883  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
30884  * @cfg {String} title          The title for this panel
30885  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
30886  * @cfg {String} url            Calls {@link #setUrl} with this value
30887  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
30888  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
30889  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
30890  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
30891
30892  * @constructor
30893  * Create a new ContentPanel.
30894  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
30895  * @param {String/Object} config A string to set only the title or a config object
30896  * @param {String} content (optional) Set the HTML content for this panel
30897  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
30898  */
30899 Roo.ContentPanel = function(el, config, content){
30900     
30901      
30902     /*
30903     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
30904         config = el;
30905         el = Roo.id();
30906     }
30907     if (config && config.parentLayout) { 
30908         el = config.parentLayout.el.createChild(); 
30909     }
30910     */
30911     if(el.autoCreate){ // xtype is available if this is called from factory
30912         config = el;
30913         el = Roo.id();
30914     }
30915     this.el = Roo.get(el);
30916     if(!this.el && config && config.autoCreate){
30917         if(typeof config.autoCreate == "object"){
30918             if(!config.autoCreate.id){
30919                 config.autoCreate.id = config.id||el;
30920             }
30921             this.el = Roo.DomHelper.append(document.body,
30922                         config.autoCreate, true);
30923         }else{
30924             this.el = Roo.DomHelper.append(document.body,
30925                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
30926         }
30927     }
30928     this.closable = false;
30929     this.loaded = false;
30930     this.active = false;
30931     if(typeof config == "string"){
30932         this.title = config;
30933     }else{
30934         Roo.apply(this, config);
30935     }
30936     
30937     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
30938         this.wrapEl = this.el.wrap();
30939         this.toolbar.container = this.el.insertSibling(false, 'before');
30940         this.toolbar = new Roo.Toolbar(this.toolbar);
30941     }
30942     
30943     // xtype created footer. - not sure if will work as we normally have to render first..
30944     if (this.footer && !this.footer.el && this.footer.xtype) {
30945         if (!this.wrapEl) {
30946             this.wrapEl = this.el.wrap();
30947         }
30948     
30949         this.footer.container = this.wrapEl.createChild();
30950          
30951         this.footer = Roo.factory(this.footer, Roo);
30952         
30953     }
30954     
30955     if(this.resizeEl){
30956         this.resizeEl = Roo.get(this.resizeEl, true);
30957     }else{
30958         this.resizeEl = this.el;
30959     }
30960     // handle view.xtype
30961     
30962  
30963     
30964     
30965     this.addEvents({
30966         /**
30967          * @event activate
30968          * Fires when this panel is activated. 
30969          * @param {Roo.ContentPanel} this
30970          */
30971         "activate" : true,
30972         /**
30973          * @event deactivate
30974          * Fires when this panel is activated. 
30975          * @param {Roo.ContentPanel} this
30976          */
30977         "deactivate" : true,
30978
30979         /**
30980          * @event resize
30981          * Fires when this panel is resized if fitToFrame is true.
30982          * @param {Roo.ContentPanel} this
30983          * @param {Number} width The width after any component adjustments
30984          * @param {Number} height The height after any component adjustments
30985          */
30986         "resize" : true,
30987         
30988          /**
30989          * @event render
30990          * Fires when this tab is created
30991          * @param {Roo.ContentPanel} this
30992          */
30993         "render" : true
30994          
30995         
30996     });
30997     
30998
30999     
31000     
31001     if(this.autoScroll){
31002         this.resizeEl.setStyle("overflow", "auto");
31003     } else {
31004         // fix randome scrolling
31005         this.el.on('scroll', function() {
31006             Roo.log('fix random scolling');
31007             this.scrollTo('top',0); 
31008         });
31009     }
31010     content = content || this.content;
31011     if(content){
31012         this.setContent(content);
31013     }
31014     if(config && config.url){
31015         this.setUrl(this.url, this.params, this.loadOnce);
31016     }
31017     
31018     
31019     
31020     Roo.ContentPanel.superclass.constructor.call(this);
31021     
31022     if (this.view && typeof(this.view.xtype) != 'undefined') {
31023         this.view.el = this.el.appendChild(document.createElement("div"));
31024         this.view = Roo.factory(this.view); 
31025         this.view.render  &&  this.view.render(false, '');  
31026     }
31027     
31028     
31029     this.fireEvent('render', this);
31030 };
31031
31032 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
31033     tabTip:'',
31034     setRegion : function(region){
31035         this.region = region;
31036         if(region){
31037            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
31038         }else{
31039            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
31040         } 
31041     },
31042     
31043     /**
31044      * Returns the toolbar for this Panel if one was configured. 
31045      * @return {Roo.Toolbar} 
31046      */
31047     getToolbar : function(){
31048         return this.toolbar;
31049     },
31050     
31051     setActiveState : function(active){
31052         this.active = active;
31053         if(!active){
31054             this.fireEvent("deactivate", this);
31055         }else{
31056             this.fireEvent("activate", this);
31057         }
31058     },
31059     /**
31060      * Updates this panel's element
31061      * @param {String} content The new content
31062      * @param {Boolean} loadScripts (optional) true to look for and process scripts
31063     */
31064     setContent : function(content, loadScripts){
31065         this.el.update(content, loadScripts);
31066     },
31067
31068     ignoreResize : function(w, h){
31069         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
31070             return true;
31071         }else{
31072             this.lastSize = {width: w, height: h};
31073             return false;
31074         }
31075     },
31076     /**
31077      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
31078      * @return {Roo.UpdateManager} The UpdateManager
31079      */
31080     getUpdateManager : function(){
31081         return this.el.getUpdateManager();
31082     },
31083      /**
31084      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
31085      * @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:
31086 <pre><code>
31087 panel.load({
31088     url: "your-url.php",
31089     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
31090     callback: yourFunction,
31091     scope: yourObject, //(optional scope)
31092     discardUrl: false,
31093     nocache: false,
31094     text: "Loading...",
31095     timeout: 30,
31096     scripts: false
31097 });
31098 </code></pre>
31099      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
31100      * 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.
31101      * @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}
31102      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
31103      * @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.
31104      * @return {Roo.ContentPanel} this
31105      */
31106     load : function(){
31107         var um = this.el.getUpdateManager();
31108         um.update.apply(um, arguments);
31109         return this;
31110     },
31111
31112
31113     /**
31114      * 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.
31115      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
31116      * @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)
31117      * @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)
31118      * @return {Roo.UpdateManager} The UpdateManager
31119      */
31120     setUrl : function(url, params, loadOnce){
31121         if(this.refreshDelegate){
31122             this.removeListener("activate", this.refreshDelegate);
31123         }
31124         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
31125         this.on("activate", this.refreshDelegate);
31126         return this.el.getUpdateManager();
31127     },
31128     
31129     _handleRefresh : function(url, params, loadOnce){
31130         if(!loadOnce || !this.loaded){
31131             var updater = this.el.getUpdateManager();
31132             updater.update(url, params, this._setLoaded.createDelegate(this));
31133         }
31134     },
31135     
31136     _setLoaded : function(){
31137         this.loaded = true;
31138     }, 
31139     
31140     /**
31141      * Returns this panel's id
31142      * @return {String} 
31143      */
31144     getId : function(){
31145         return this.el.id;
31146     },
31147     
31148     /** 
31149      * Returns this panel's element - used by regiosn to add.
31150      * @return {Roo.Element} 
31151      */
31152     getEl : function(){
31153         return this.wrapEl || this.el;
31154     },
31155     
31156     adjustForComponents : function(width, height)
31157     {
31158         //Roo.log('adjustForComponents ');
31159         if(this.resizeEl != this.el){
31160             width -= this.el.getFrameWidth('lr');
31161             height -= this.el.getFrameWidth('tb');
31162         }
31163         if(this.toolbar){
31164             var te = this.toolbar.getEl();
31165             height -= te.getHeight();
31166             te.setWidth(width);
31167         }
31168         if(this.footer){
31169             var te = this.footer.getEl();
31170             //Roo.log("footer:" + te.getHeight());
31171             
31172             height -= te.getHeight();
31173             te.setWidth(width);
31174         }
31175         
31176         
31177         if(this.adjustments){
31178             width += this.adjustments[0];
31179             height += this.adjustments[1];
31180         }
31181         return {"width": width, "height": height};
31182     },
31183     
31184     setSize : function(width, height){
31185         if(this.fitToFrame && !this.ignoreResize(width, height)){
31186             if(this.fitContainer && this.resizeEl != this.el){
31187                 this.el.setSize(width, height);
31188             }
31189             var size = this.adjustForComponents(width, height);
31190             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
31191             this.fireEvent('resize', this, size.width, size.height);
31192         }
31193     },
31194     
31195     /**
31196      * Returns this panel's title
31197      * @return {String} 
31198      */
31199     getTitle : function(){
31200         return this.title;
31201     },
31202     
31203     /**
31204      * Set this panel's title
31205      * @param {String} title
31206      */
31207     setTitle : function(title){
31208         this.title = title;
31209         if(this.region){
31210             this.region.updatePanelTitle(this, title);
31211         }
31212     },
31213     
31214     /**
31215      * Returns true is this panel was configured to be closable
31216      * @return {Boolean} 
31217      */
31218     isClosable : function(){
31219         return this.closable;
31220     },
31221     
31222     beforeSlide : function(){
31223         this.el.clip();
31224         this.resizeEl.clip();
31225     },
31226     
31227     afterSlide : function(){
31228         this.el.unclip();
31229         this.resizeEl.unclip();
31230     },
31231     
31232     /**
31233      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
31234      *   Will fail silently if the {@link #setUrl} method has not been called.
31235      *   This does not activate the panel, just updates its content.
31236      */
31237     refresh : function(){
31238         if(this.refreshDelegate){
31239            this.loaded = false;
31240            this.refreshDelegate();
31241         }
31242     },
31243     
31244     /**
31245      * Destroys this panel
31246      */
31247     destroy : function(){
31248         this.el.removeAllListeners();
31249         var tempEl = document.createElement("span");
31250         tempEl.appendChild(this.el.dom);
31251         tempEl.innerHTML = "";
31252         this.el.remove();
31253         this.el = null;
31254     },
31255     
31256     /**
31257      * form - if the content panel contains a form - this is a reference to it.
31258      * @type {Roo.form.Form}
31259      */
31260     form : false,
31261     /**
31262      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
31263      *    This contains a reference to it.
31264      * @type {Roo.View}
31265      */
31266     view : false,
31267     
31268       /**
31269      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
31270      * <pre><code>
31271
31272 layout.addxtype({
31273        xtype : 'Form',
31274        items: [ .... ]
31275    }
31276 );
31277
31278 </code></pre>
31279      * @param {Object} cfg Xtype definition of item to add.
31280      */
31281     
31282     addxtype : function(cfg) {
31283         // add form..
31284         if (cfg.xtype.match(/^Form$/)) {
31285             
31286             var el;
31287             //if (this.footer) {
31288             //    el = this.footer.container.insertSibling(false, 'before');
31289             //} else {
31290                 el = this.el.createChild();
31291             //}
31292
31293             this.form = new  Roo.form.Form(cfg);
31294             
31295             
31296             if ( this.form.allItems.length) {
31297                 this.form.render(el.dom);
31298             }
31299             return this.form;
31300         }
31301         // should only have one of theses..
31302         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
31303             // views.. should not be just added - used named prop 'view''
31304             
31305             cfg.el = this.el.appendChild(document.createElement("div"));
31306             // factory?
31307             
31308             var ret = new Roo.factory(cfg);
31309              
31310              ret.render && ret.render(false, ''); // render blank..
31311             this.view = ret;
31312             return ret;
31313         }
31314         return false;
31315     }
31316 });
31317
31318 /**
31319  * @class Roo.GridPanel
31320  * @extends Roo.ContentPanel
31321  * @constructor
31322  * Create a new GridPanel.
31323  * @param {Roo.grid.Grid} grid The grid for this panel
31324  * @param {String/Object} config A string to set only the panel's title, or a config object
31325  */
31326 Roo.GridPanel = function(grid, config){
31327     
31328   
31329     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
31330         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
31331         
31332     this.wrapper.dom.appendChild(grid.getGridEl().dom);
31333     
31334     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
31335     
31336     if(this.toolbar){
31337         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
31338     }
31339     // xtype created footer. - not sure if will work as we normally have to render first..
31340     if (this.footer && !this.footer.el && this.footer.xtype) {
31341         
31342         this.footer.container = this.grid.getView().getFooterPanel(true);
31343         this.footer.dataSource = this.grid.dataSource;
31344         this.footer = Roo.factory(this.footer, Roo);
31345         
31346     }
31347     
31348     grid.monitorWindowResize = false; // turn off autosizing
31349     grid.autoHeight = false;
31350     grid.autoWidth = false;
31351     this.grid = grid;
31352     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
31353 };
31354
31355 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
31356     getId : function(){
31357         return this.grid.id;
31358     },
31359     
31360     /**
31361      * Returns the grid for this panel
31362      * @return {Roo.grid.Grid} 
31363      */
31364     getGrid : function(){
31365         return this.grid;    
31366     },
31367     
31368     setSize : function(width, height){
31369         if(!this.ignoreResize(width, height)){
31370             var grid = this.grid;
31371             var size = this.adjustForComponents(width, height);
31372             grid.getGridEl().setSize(size.width, size.height);
31373             grid.autoSize();
31374         }
31375     },
31376     
31377     beforeSlide : function(){
31378         this.grid.getView().scroller.clip();
31379     },
31380     
31381     afterSlide : function(){
31382         this.grid.getView().scroller.unclip();
31383     },
31384     
31385     destroy : function(){
31386         this.grid.destroy();
31387         delete this.grid;
31388         Roo.GridPanel.superclass.destroy.call(this); 
31389     }
31390 });
31391
31392
31393 /**
31394  * @class Roo.NestedLayoutPanel
31395  * @extends Roo.ContentPanel
31396  * @constructor
31397  * Create a new NestedLayoutPanel.
31398  * 
31399  * 
31400  * @param {Roo.BorderLayout} layout The layout for this panel
31401  * @param {String/Object} config A string to set only the title or a config object
31402  */
31403 Roo.NestedLayoutPanel = function(layout, config)
31404 {
31405     // construct with only one argument..
31406     /* FIXME - implement nicer consturctors
31407     if (layout.layout) {
31408         config = layout;
31409         layout = config.layout;
31410         delete config.layout;
31411     }
31412     if (layout.xtype && !layout.getEl) {
31413         // then layout needs constructing..
31414         layout = Roo.factory(layout, Roo);
31415     }
31416     */
31417     
31418     
31419     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
31420     
31421     layout.monitorWindowResize = false; // turn off autosizing
31422     this.layout = layout;
31423     this.layout.getEl().addClass("x-layout-nested-layout");
31424     
31425     
31426     
31427     
31428 };
31429
31430 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
31431
31432     setSize : function(width, height){
31433         if(!this.ignoreResize(width, height)){
31434             var size = this.adjustForComponents(width, height);
31435             var el = this.layout.getEl();
31436             el.setSize(size.width, size.height);
31437             var touch = el.dom.offsetWidth;
31438             this.layout.layout();
31439             // ie requires a double layout on the first pass
31440             if(Roo.isIE && !this.initialized){
31441                 this.initialized = true;
31442                 this.layout.layout();
31443             }
31444         }
31445     },
31446     
31447     // activate all subpanels if not currently active..
31448     
31449     setActiveState : function(active){
31450         this.active = active;
31451         if(!active){
31452             this.fireEvent("deactivate", this);
31453             return;
31454         }
31455         
31456         this.fireEvent("activate", this);
31457         // not sure if this should happen before or after..
31458         if (!this.layout) {
31459             return; // should not happen..
31460         }
31461         var reg = false;
31462         for (var r in this.layout.regions) {
31463             reg = this.layout.getRegion(r);
31464             if (reg.getActivePanel()) {
31465                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
31466                 reg.setActivePanel(reg.getActivePanel());
31467                 continue;
31468             }
31469             if (!reg.panels.length) {
31470                 continue;
31471             }
31472             reg.showPanel(reg.getPanel(0));
31473         }
31474         
31475         
31476         
31477         
31478     },
31479     
31480     /**
31481      * Returns the nested BorderLayout for this panel
31482      * @return {Roo.BorderLayout} 
31483      */
31484     getLayout : function(){
31485         return this.layout;
31486     },
31487     
31488      /**
31489      * Adds a xtype elements to the layout of the nested panel
31490      * <pre><code>
31491
31492 panel.addxtype({
31493        xtype : 'ContentPanel',
31494        region: 'west',
31495        items: [ .... ]
31496    }
31497 );
31498
31499 panel.addxtype({
31500         xtype : 'NestedLayoutPanel',
31501         region: 'west',
31502         layout: {
31503            center: { },
31504            west: { }   
31505         },
31506         items : [ ... list of content panels or nested layout panels.. ]
31507    }
31508 );
31509 </code></pre>
31510      * @param {Object} cfg Xtype definition of item to add.
31511      */
31512     addxtype : function(cfg) {
31513         return this.layout.addxtype(cfg);
31514     
31515     }
31516 });
31517
31518 Roo.ScrollPanel = function(el, config, content){
31519     config = config || {};
31520     config.fitToFrame = true;
31521     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
31522     
31523     this.el.dom.style.overflow = "hidden";
31524     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
31525     this.el.removeClass("x-layout-inactive-content");
31526     this.el.on("mousewheel", this.onWheel, this);
31527
31528     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
31529     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
31530     up.unselectable(); down.unselectable();
31531     up.on("click", this.scrollUp, this);
31532     down.on("click", this.scrollDown, this);
31533     up.addClassOnOver("x-scroller-btn-over");
31534     down.addClassOnOver("x-scroller-btn-over");
31535     up.addClassOnClick("x-scroller-btn-click");
31536     down.addClassOnClick("x-scroller-btn-click");
31537     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
31538
31539     this.resizeEl = this.el;
31540     this.el = wrap; this.up = up; this.down = down;
31541 };
31542
31543 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
31544     increment : 100,
31545     wheelIncrement : 5,
31546     scrollUp : function(){
31547         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
31548     },
31549
31550     scrollDown : function(){
31551         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
31552     },
31553
31554     afterScroll : function(){
31555         var el = this.resizeEl;
31556         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
31557         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31558         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31559     },
31560
31561     setSize : function(){
31562         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
31563         this.afterScroll();
31564     },
31565
31566     onWheel : function(e){
31567         var d = e.getWheelDelta();
31568         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
31569         this.afterScroll();
31570         e.stopEvent();
31571     },
31572
31573     setContent : function(content, loadScripts){
31574         this.resizeEl.update(content, loadScripts);
31575     }
31576
31577 });
31578
31579
31580
31581
31582
31583
31584
31585
31586
31587 /**
31588  * @class Roo.TreePanel
31589  * @extends Roo.ContentPanel
31590  * @constructor
31591  * Create a new TreePanel. - defaults to fit/scoll contents.
31592  * @param {String/Object} config A string to set only the panel's title, or a config object
31593  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
31594  */
31595 Roo.TreePanel = function(config){
31596     var el = config.el;
31597     var tree = config.tree;
31598     delete config.tree; 
31599     delete config.el; // hopefull!
31600     
31601     // wrapper for IE7 strict & safari scroll issue
31602     
31603     var treeEl = el.createChild();
31604     config.resizeEl = treeEl;
31605     
31606     
31607     
31608     Roo.TreePanel.superclass.constructor.call(this, el, config);
31609  
31610  
31611     this.tree = new Roo.tree.TreePanel(treeEl , tree);
31612     //console.log(tree);
31613     this.on('activate', function()
31614     {
31615         if (this.tree.rendered) {
31616             return;
31617         }
31618         //console.log('render tree');
31619         this.tree.render();
31620     });
31621     // this should not be needed.. - it's actually the 'el' that resizes?
31622     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
31623     
31624     //this.on('resize',  function (cp, w, h) {
31625     //        this.tree.innerCt.setWidth(w);
31626     //        this.tree.innerCt.setHeight(h);
31627     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
31628     //});
31629
31630         
31631     
31632 };
31633
31634 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
31635     fitToFrame : true,
31636     autoScroll : true
31637 });
31638
31639
31640
31641
31642
31643
31644
31645
31646
31647
31648
31649 /*
31650  * Based on:
31651  * Ext JS Library 1.1.1
31652  * Copyright(c) 2006-2007, Ext JS, LLC.
31653  *
31654  * Originally Released Under LGPL - original licence link has changed is not relivant.
31655  *
31656  * Fork - LGPL
31657  * <script type="text/javascript">
31658  */
31659  
31660
31661 /**
31662  * @class Roo.ReaderLayout
31663  * @extends Roo.BorderLayout
31664  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
31665  * center region containing two nested regions (a top one for a list view and one for item preview below),
31666  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
31667  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
31668  * expedites the setup of the overall layout and regions for this common application style.
31669  * Example:
31670  <pre><code>
31671 var reader = new Roo.ReaderLayout();
31672 var CP = Roo.ContentPanel;  // shortcut for adding
31673
31674 reader.beginUpdate();
31675 reader.add("north", new CP("north", "North"));
31676 reader.add("west", new CP("west", {title: "West"}));
31677 reader.add("east", new CP("east", {title: "East"}));
31678
31679 reader.regions.listView.add(new CP("listView", "List"));
31680 reader.regions.preview.add(new CP("preview", "Preview"));
31681 reader.endUpdate();
31682 </code></pre>
31683 * @constructor
31684 * Create a new ReaderLayout
31685 * @param {Object} config Configuration options
31686 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
31687 * document.body if omitted)
31688 */
31689 Roo.ReaderLayout = function(config, renderTo){
31690     var c = config || {size:{}};
31691     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
31692         north: c.north !== false ? Roo.apply({
31693             split:false,
31694             initialSize: 32,
31695             titlebar: false
31696         }, c.north) : false,
31697         west: c.west !== false ? Roo.apply({
31698             split:true,
31699             initialSize: 200,
31700             minSize: 175,
31701             maxSize: 400,
31702             titlebar: true,
31703             collapsible: true,
31704             animate: true,
31705             margins:{left:5,right:0,bottom:5,top:5},
31706             cmargins:{left:5,right:5,bottom:5,top:5}
31707         }, c.west) : false,
31708         east: c.east !== false ? Roo.apply({
31709             split:true,
31710             initialSize: 200,
31711             minSize: 175,
31712             maxSize: 400,
31713             titlebar: true,
31714             collapsible: true,
31715             animate: true,
31716             margins:{left:0,right:5,bottom:5,top:5},
31717             cmargins:{left:5,right:5,bottom:5,top:5}
31718         }, c.east) : false,
31719         center: Roo.apply({
31720             tabPosition: 'top',
31721             autoScroll:false,
31722             closeOnTab: true,
31723             titlebar:false,
31724             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
31725         }, c.center)
31726     });
31727
31728     this.el.addClass('x-reader');
31729
31730     this.beginUpdate();
31731
31732     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
31733         south: c.preview !== false ? Roo.apply({
31734             split:true,
31735             initialSize: 200,
31736             minSize: 100,
31737             autoScroll:true,
31738             collapsible:true,
31739             titlebar: true,
31740             cmargins:{top:5,left:0, right:0, bottom:0}
31741         }, c.preview) : false,
31742         center: Roo.apply({
31743             autoScroll:false,
31744             titlebar:false,
31745             minHeight:200
31746         }, c.listView)
31747     });
31748     this.add('center', new Roo.NestedLayoutPanel(inner,
31749             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
31750
31751     this.endUpdate();
31752
31753     this.regions.preview = inner.getRegion('south');
31754     this.regions.listView = inner.getRegion('center');
31755 };
31756
31757 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
31758  * Based on:
31759  * Ext JS Library 1.1.1
31760  * Copyright(c) 2006-2007, Ext JS, LLC.
31761  *
31762  * Originally Released Under LGPL - original licence link has changed is not relivant.
31763  *
31764  * Fork - LGPL
31765  * <script type="text/javascript">
31766  */
31767  
31768 /**
31769  * @class Roo.grid.Grid
31770  * @extends Roo.util.Observable
31771  * This class represents the primary interface of a component based grid control.
31772  * <br><br>Usage:<pre><code>
31773  var grid = new Roo.grid.Grid("my-container-id", {
31774      ds: myDataStore,
31775      cm: myColModel,
31776      selModel: mySelectionModel,
31777      autoSizeColumns: true,
31778      monitorWindowResize: false,
31779      trackMouseOver: true
31780  });
31781  // set any options
31782  grid.render();
31783  * </code></pre>
31784  * <b>Common Problems:</b><br/>
31785  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
31786  * element will correct this<br/>
31787  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
31788  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
31789  * are unpredictable.<br/>
31790  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
31791  * grid to calculate dimensions/offsets.<br/>
31792   * @constructor
31793  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
31794  * The container MUST have some type of size defined for the grid to fill. The container will be
31795  * automatically set to position relative if it isn't already.
31796  * @param {Object} config A config object that sets properties on this grid.
31797  */
31798 Roo.grid.Grid = function(container, config){
31799         // initialize the container
31800         this.container = Roo.get(container);
31801         this.container.update("");
31802         this.container.setStyle("overflow", "hidden");
31803     this.container.addClass('x-grid-container');
31804
31805     this.id = this.container.id;
31806
31807     Roo.apply(this, config);
31808     // check and correct shorthanded configs
31809     if(this.ds){
31810         this.dataSource = this.ds;
31811         delete this.ds;
31812     }
31813     if(this.cm){
31814         this.colModel = this.cm;
31815         delete this.cm;
31816     }
31817     if(this.sm){
31818         this.selModel = this.sm;
31819         delete this.sm;
31820     }
31821
31822     if (this.selModel) {
31823         this.selModel = Roo.factory(this.selModel, Roo.grid);
31824         this.sm = this.selModel;
31825         this.sm.xmodule = this.xmodule || false;
31826     }
31827     if (typeof(this.colModel.config) == 'undefined') {
31828         this.colModel = new Roo.grid.ColumnModel(this.colModel);
31829         this.cm = this.colModel;
31830         this.cm.xmodule = this.xmodule || false;
31831     }
31832     if (this.dataSource) {
31833         this.dataSource= Roo.factory(this.dataSource, Roo.data);
31834         this.ds = this.dataSource;
31835         this.ds.xmodule = this.xmodule || false;
31836          
31837     }
31838     
31839     
31840     
31841     if(this.width){
31842         this.container.setWidth(this.width);
31843     }
31844
31845     if(this.height){
31846         this.container.setHeight(this.height);
31847     }
31848     /** @private */
31849         this.addEvents({
31850         // raw events
31851         /**
31852          * @event click
31853          * The raw click event for the entire grid.
31854          * @param {Roo.EventObject} e
31855          */
31856         "click" : true,
31857         /**
31858          * @event dblclick
31859          * The raw dblclick event for the entire grid.
31860          * @param {Roo.EventObject} e
31861          */
31862         "dblclick" : true,
31863         /**
31864          * @event contextmenu
31865          * The raw contextmenu event for the entire grid.
31866          * @param {Roo.EventObject} e
31867          */
31868         "contextmenu" : true,
31869         /**
31870          * @event mousedown
31871          * The raw mousedown event for the entire grid.
31872          * @param {Roo.EventObject} e
31873          */
31874         "mousedown" : true,
31875         /**
31876          * @event mouseup
31877          * The raw mouseup event for the entire grid.
31878          * @param {Roo.EventObject} e
31879          */
31880         "mouseup" : true,
31881         /**
31882          * @event mouseover
31883          * The raw mouseover event for the entire grid.
31884          * @param {Roo.EventObject} e
31885          */
31886         "mouseover" : true,
31887         /**
31888          * @event mouseout
31889          * The raw mouseout event for the entire grid.
31890          * @param {Roo.EventObject} e
31891          */
31892         "mouseout" : true,
31893         /**
31894          * @event keypress
31895          * The raw keypress event for the entire grid.
31896          * @param {Roo.EventObject} e
31897          */
31898         "keypress" : true,
31899         /**
31900          * @event keydown
31901          * The raw keydown event for the entire grid.
31902          * @param {Roo.EventObject} e
31903          */
31904         "keydown" : true,
31905
31906         // custom events
31907
31908         /**
31909          * @event cellclick
31910          * Fires when a cell is clicked
31911          * @param {Grid} this
31912          * @param {Number} rowIndex
31913          * @param {Number} columnIndex
31914          * @param {Roo.EventObject} e
31915          */
31916         "cellclick" : true,
31917         /**
31918          * @event celldblclick
31919          * Fires when a cell is double clicked
31920          * @param {Grid} this
31921          * @param {Number} rowIndex
31922          * @param {Number} columnIndex
31923          * @param {Roo.EventObject} e
31924          */
31925         "celldblclick" : true,
31926         /**
31927          * @event rowclick
31928          * Fires when a row is clicked
31929          * @param {Grid} this
31930          * @param {Number} rowIndex
31931          * @param {Roo.EventObject} e
31932          */
31933         "rowclick" : true,
31934         /**
31935          * @event rowdblclick
31936          * Fires when a row is double clicked
31937          * @param {Grid} this
31938          * @param {Number} rowIndex
31939          * @param {Roo.EventObject} e
31940          */
31941         "rowdblclick" : true,
31942         /**
31943          * @event headerclick
31944          * Fires when a header is clicked
31945          * @param {Grid} this
31946          * @param {Number} columnIndex
31947          * @param {Roo.EventObject} e
31948          */
31949         "headerclick" : true,
31950         /**
31951          * @event headerdblclick
31952          * Fires when a header cell is double clicked
31953          * @param {Grid} this
31954          * @param {Number} columnIndex
31955          * @param {Roo.EventObject} e
31956          */
31957         "headerdblclick" : true,
31958         /**
31959          * @event rowcontextmenu
31960          * Fires when a row is right clicked
31961          * @param {Grid} this
31962          * @param {Number} rowIndex
31963          * @param {Roo.EventObject} e
31964          */
31965         "rowcontextmenu" : true,
31966         /**
31967          * @event cellcontextmenu
31968          * Fires when a cell is right clicked
31969          * @param {Grid} this
31970          * @param {Number} rowIndex
31971          * @param {Number} cellIndex
31972          * @param {Roo.EventObject} e
31973          */
31974          "cellcontextmenu" : true,
31975         /**
31976          * @event headercontextmenu
31977          * Fires when a header is right clicked
31978          * @param {Grid} this
31979          * @param {Number} columnIndex
31980          * @param {Roo.EventObject} e
31981          */
31982         "headercontextmenu" : true,
31983         /**
31984          * @event bodyscroll
31985          * Fires when the body element is scrolled
31986          * @param {Number} scrollLeft
31987          * @param {Number} scrollTop
31988          */
31989         "bodyscroll" : true,
31990         /**
31991          * @event columnresize
31992          * Fires when the user resizes a column
31993          * @param {Number} columnIndex
31994          * @param {Number} newSize
31995          */
31996         "columnresize" : true,
31997         /**
31998          * @event columnmove
31999          * Fires when the user moves a column
32000          * @param {Number} oldIndex
32001          * @param {Number} newIndex
32002          */
32003         "columnmove" : true,
32004         /**
32005          * @event startdrag
32006          * Fires when row(s) start being dragged
32007          * @param {Grid} this
32008          * @param {Roo.GridDD} dd The drag drop object
32009          * @param {event} e The raw browser event
32010          */
32011         "startdrag" : true,
32012         /**
32013          * @event enddrag
32014          * Fires when a drag operation is complete
32015          * @param {Grid} this
32016          * @param {Roo.GridDD} dd The drag drop object
32017          * @param {event} e The raw browser event
32018          */
32019         "enddrag" : true,
32020         /**
32021          * @event dragdrop
32022          * Fires when dragged row(s) are dropped on a valid DD target
32023          * @param {Grid} this
32024          * @param {Roo.GridDD} dd The drag drop object
32025          * @param {String} targetId The target drag drop object
32026          * @param {event} e The raw browser event
32027          */
32028         "dragdrop" : true,
32029         /**
32030          * @event dragover
32031          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
32032          * @param {Grid} this
32033          * @param {Roo.GridDD} dd The drag drop object
32034          * @param {String} targetId The target drag drop object
32035          * @param {event} e The raw browser event
32036          */
32037         "dragover" : true,
32038         /**
32039          * @event dragenter
32040          *  Fires when the dragged row(s) first cross another DD target while being dragged
32041          * @param {Grid} this
32042          * @param {Roo.GridDD} dd The drag drop object
32043          * @param {String} targetId The target drag drop object
32044          * @param {event} e The raw browser event
32045          */
32046         "dragenter" : true,
32047         /**
32048          * @event dragout
32049          * Fires when the dragged row(s) leave another DD target while being dragged
32050          * @param {Grid} this
32051          * @param {Roo.GridDD} dd The drag drop object
32052          * @param {String} targetId The target drag drop object
32053          * @param {event} e The raw browser event
32054          */
32055         "dragout" : true,
32056         /**
32057          * @event rowclass
32058          * Fires when a row is rendered, so you can change add a style to it.
32059          * @param {GridView} gridview   The grid view
32060          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
32061          */
32062         'rowclass' : true,
32063
32064         /**
32065          * @event render
32066          * Fires when the grid is rendered
32067          * @param {Grid} grid
32068          */
32069         'render' : true
32070     });
32071
32072     Roo.grid.Grid.superclass.constructor.call(this);
32073 };
32074 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
32075     
32076     /**
32077      * @cfg {String} ddGroup - drag drop group.
32078      */
32079
32080     /**
32081      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
32082      */
32083     minColumnWidth : 25,
32084
32085     /**
32086      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
32087      * <b>on initial render.</b> It is more efficient to explicitly size the columns
32088      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
32089      */
32090     autoSizeColumns : false,
32091
32092     /**
32093      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
32094      */
32095     autoSizeHeaders : true,
32096
32097     /**
32098      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
32099      */
32100     monitorWindowResize : true,
32101
32102     /**
32103      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
32104      * rows measured to get a columns size. Default is 0 (all rows).
32105      */
32106     maxRowsToMeasure : 0,
32107
32108     /**
32109      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
32110      */
32111     trackMouseOver : true,
32112
32113     /**
32114     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
32115     */
32116     
32117     /**
32118     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
32119     */
32120     enableDragDrop : false,
32121     
32122     /**
32123     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
32124     */
32125     enableColumnMove : true,
32126     
32127     /**
32128     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
32129     */
32130     enableColumnHide : true,
32131     
32132     /**
32133     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
32134     */
32135     enableRowHeightSync : false,
32136     
32137     /**
32138     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
32139     */
32140     stripeRows : true,
32141     
32142     /**
32143     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
32144     */
32145     autoHeight : false,
32146
32147     /**
32148      * @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.
32149      */
32150     autoExpandColumn : false,
32151
32152     /**
32153     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
32154     * Default is 50.
32155     */
32156     autoExpandMin : 50,
32157
32158     /**
32159     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
32160     */
32161     autoExpandMax : 1000,
32162
32163     /**
32164     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
32165     */
32166     view : null,
32167
32168     /**
32169     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
32170     */
32171     loadMask : false,
32172     /**
32173     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
32174     */
32175     dropTarget: false,
32176     
32177    
32178     
32179     // private
32180     rendered : false,
32181
32182     /**
32183     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
32184     * of a fixed width. Default is false.
32185     */
32186     /**
32187     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
32188     */
32189     /**
32190      * Called once after all setup has been completed and the grid is ready to be rendered.
32191      * @return {Roo.grid.Grid} this
32192      */
32193     render : function()
32194     {
32195         var c = this.container;
32196         // try to detect autoHeight/width mode
32197         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
32198             this.autoHeight = true;
32199         }
32200         var view = this.getView();
32201         view.init(this);
32202
32203         c.on("click", this.onClick, this);
32204         c.on("dblclick", this.onDblClick, this);
32205         c.on("contextmenu", this.onContextMenu, this);
32206         c.on("keydown", this.onKeyDown, this);
32207         if (Roo.isTouch) {
32208             c.on("touchstart", this.onTouchStart, this);
32209         }
32210
32211         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
32212
32213         this.getSelectionModel().init(this);
32214
32215         view.render();
32216
32217         if(this.loadMask){
32218             this.loadMask = new Roo.LoadMask(this.container,
32219                     Roo.apply({store:this.dataSource}, this.loadMask));
32220         }
32221         
32222         
32223         if (this.toolbar && this.toolbar.xtype) {
32224             this.toolbar.container = this.getView().getHeaderPanel(true);
32225             this.toolbar = new Roo.Toolbar(this.toolbar);
32226         }
32227         if (this.footer && this.footer.xtype) {
32228             this.footer.dataSource = this.getDataSource();
32229             this.footer.container = this.getView().getFooterPanel(true);
32230             this.footer = Roo.factory(this.footer, Roo);
32231         }
32232         if (this.dropTarget && this.dropTarget.xtype) {
32233             delete this.dropTarget.xtype;
32234             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
32235         }
32236         
32237         
32238         this.rendered = true;
32239         this.fireEvent('render', this);
32240         return this;
32241     },
32242
32243         /**
32244          * Reconfigures the grid to use a different Store and Column Model.
32245          * The View will be bound to the new objects and refreshed.
32246          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
32247          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
32248          */
32249     reconfigure : function(dataSource, colModel){
32250         if(this.loadMask){
32251             this.loadMask.destroy();
32252             this.loadMask = new Roo.LoadMask(this.container,
32253                     Roo.apply({store:dataSource}, this.loadMask));
32254         }
32255         this.view.bind(dataSource, colModel);
32256         this.dataSource = dataSource;
32257         this.colModel = colModel;
32258         this.view.refresh(true);
32259     },
32260
32261     // private
32262     onKeyDown : function(e){
32263         this.fireEvent("keydown", e);
32264     },
32265
32266     /**
32267      * Destroy this grid.
32268      * @param {Boolean} removeEl True to remove the element
32269      */
32270     destroy : function(removeEl, keepListeners){
32271         if(this.loadMask){
32272             this.loadMask.destroy();
32273         }
32274         var c = this.container;
32275         c.removeAllListeners();
32276         this.view.destroy();
32277         this.colModel.purgeListeners();
32278         if(!keepListeners){
32279             this.purgeListeners();
32280         }
32281         c.update("");
32282         if(removeEl === true){
32283             c.remove();
32284         }
32285     },
32286
32287     // private
32288     processEvent : function(name, e){
32289         // does this fire select???
32290         //Roo.log('grid:processEvent '  + name);
32291         
32292         if (name != 'touchstart' ) {
32293             this.fireEvent(name, e);    
32294         }
32295         
32296         var t = e.getTarget();
32297         var v = this.view;
32298         var header = v.findHeaderIndex(t);
32299         if(header !== false){
32300             var ename = name == 'touchstart' ? 'click' : name;
32301              
32302             this.fireEvent("header" + ename, this, header, e);
32303         }else{
32304             var row = v.findRowIndex(t);
32305             var cell = v.findCellIndex(t);
32306             if (name == 'touchstart') {
32307                 // first touch is always a click.
32308                 // hopefull this happens after selection is updated.?
32309                 name = false;
32310                 
32311                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
32312                     var cs = this.selModel.getSelectedCell();
32313                     if (row == cs[0] && cell == cs[1]){
32314                         name = 'dblclick';
32315                     }
32316                 }
32317                 if (typeof(this.selModel.getSelections) != 'undefined') {
32318                     var cs = this.selModel.getSelections();
32319                     var ds = this.dataSource;
32320                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
32321                         name = 'dblclick';
32322                     }
32323                 }
32324                 if (!name) {
32325                     return;
32326                 }
32327             }
32328             
32329             
32330             if(row !== false){
32331                 this.fireEvent("row" + name, this, row, e);
32332                 if(cell !== false){
32333                     this.fireEvent("cell" + name, this, row, cell, e);
32334                 }
32335             }
32336         }
32337     },
32338
32339     // private
32340     onClick : function(e){
32341         this.processEvent("click", e);
32342     },
32343    // private
32344     onTouchStart : function(e){
32345         this.processEvent("touchstart", e);
32346     },
32347
32348     // private
32349     onContextMenu : function(e, t){
32350         this.processEvent("contextmenu", e);
32351     },
32352
32353     // private
32354     onDblClick : function(e){
32355         this.processEvent("dblclick", e);
32356     },
32357
32358     // private
32359     walkCells : function(row, col, step, fn, scope){
32360         var cm = this.colModel, clen = cm.getColumnCount();
32361         var ds = this.dataSource, rlen = ds.getCount(), first = true;
32362         if(step < 0){
32363             if(col < 0){
32364                 row--;
32365                 first = false;
32366             }
32367             while(row >= 0){
32368                 if(!first){
32369                     col = clen-1;
32370                 }
32371                 first = false;
32372                 while(col >= 0){
32373                     if(fn.call(scope || this, row, col, cm) === true){
32374                         return [row, col];
32375                     }
32376                     col--;
32377                 }
32378                 row--;
32379             }
32380         } else {
32381             if(col >= clen){
32382                 row++;
32383                 first = false;
32384             }
32385             while(row < rlen){
32386                 if(!first){
32387                     col = 0;
32388                 }
32389                 first = false;
32390                 while(col < clen){
32391                     if(fn.call(scope || this, row, col, cm) === true){
32392                         return [row, col];
32393                     }
32394                     col++;
32395                 }
32396                 row++;
32397             }
32398         }
32399         return null;
32400     },
32401
32402     // private
32403     getSelections : function(){
32404         return this.selModel.getSelections();
32405     },
32406
32407     /**
32408      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
32409      * but if manual update is required this method will initiate it.
32410      */
32411     autoSize : function(){
32412         if(this.rendered){
32413             this.view.layout();
32414             if(this.view.adjustForScroll){
32415                 this.view.adjustForScroll();
32416             }
32417         }
32418     },
32419
32420     /**
32421      * Returns the grid's underlying element.
32422      * @return {Element} The element
32423      */
32424     getGridEl : function(){
32425         return this.container;
32426     },
32427
32428     // private for compatibility, overridden by editor grid
32429     stopEditing : function(){},
32430
32431     /**
32432      * Returns the grid's SelectionModel.
32433      * @return {SelectionModel}
32434      */
32435     getSelectionModel : function(){
32436         if(!this.selModel){
32437             this.selModel = new Roo.grid.RowSelectionModel();
32438         }
32439         return this.selModel;
32440     },
32441
32442     /**
32443      * Returns the grid's DataSource.
32444      * @return {DataSource}
32445      */
32446     getDataSource : function(){
32447         return this.dataSource;
32448     },
32449
32450     /**
32451      * Returns the grid's ColumnModel.
32452      * @return {ColumnModel}
32453      */
32454     getColumnModel : function(){
32455         return this.colModel;
32456     },
32457
32458     /**
32459      * Returns the grid's GridView object.
32460      * @return {GridView}
32461      */
32462     getView : function(){
32463         if(!this.view){
32464             this.view = new Roo.grid.GridView(this.viewConfig);
32465         }
32466         return this.view;
32467     },
32468     /**
32469      * Called to get grid's drag proxy text, by default returns this.ddText.
32470      * @return {String}
32471      */
32472     getDragDropText : function(){
32473         var count = this.selModel.getCount();
32474         return String.format(this.ddText, count, count == 1 ? '' : 's');
32475     }
32476 });
32477 /**
32478  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
32479  * %0 is replaced with the number of selected rows.
32480  * @type String
32481  */
32482 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
32483  * Based on:
32484  * Ext JS Library 1.1.1
32485  * Copyright(c) 2006-2007, Ext JS, LLC.
32486  *
32487  * Originally Released Under LGPL - original licence link has changed is not relivant.
32488  *
32489  * Fork - LGPL
32490  * <script type="text/javascript">
32491  */
32492  
32493 Roo.grid.AbstractGridView = function(){
32494         this.grid = null;
32495         
32496         this.events = {
32497             "beforerowremoved" : true,
32498             "beforerowsinserted" : true,
32499             "beforerefresh" : true,
32500             "rowremoved" : true,
32501             "rowsinserted" : true,
32502             "rowupdated" : true,
32503             "refresh" : true
32504         };
32505     Roo.grid.AbstractGridView.superclass.constructor.call(this);
32506 };
32507
32508 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
32509     rowClass : "x-grid-row",
32510     cellClass : "x-grid-cell",
32511     tdClass : "x-grid-td",
32512     hdClass : "x-grid-hd",
32513     splitClass : "x-grid-hd-split",
32514     
32515     init: function(grid){
32516         this.grid = grid;
32517                 var cid = this.grid.getGridEl().id;
32518         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
32519         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
32520         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
32521         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
32522         },
32523         
32524     getColumnRenderers : function(){
32525         var renderers = [];
32526         var cm = this.grid.colModel;
32527         var colCount = cm.getColumnCount();
32528         for(var i = 0; i < colCount; i++){
32529             renderers[i] = cm.getRenderer(i);
32530         }
32531         return renderers;
32532     },
32533     
32534     getColumnIds : function(){
32535         var ids = [];
32536         var cm = this.grid.colModel;
32537         var colCount = cm.getColumnCount();
32538         for(var i = 0; i < colCount; i++){
32539             ids[i] = cm.getColumnId(i);
32540         }
32541         return ids;
32542     },
32543     
32544     getDataIndexes : function(){
32545         if(!this.indexMap){
32546             this.indexMap = this.buildIndexMap();
32547         }
32548         return this.indexMap.colToData;
32549     },
32550     
32551     getColumnIndexByDataIndex : function(dataIndex){
32552         if(!this.indexMap){
32553             this.indexMap = this.buildIndexMap();
32554         }
32555         return this.indexMap.dataToCol[dataIndex];
32556     },
32557     
32558     /**
32559      * Set a css style for a column dynamically. 
32560      * @param {Number} colIndex The index of the column
32561      * @param {String} name The css property name
32562      * @param {String} value The css value
32563      */
32564     setCSSStyle : function(colIndex, name, value){
32565         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
32566         Roo.util.CSS.updateRule(selector, name, value);
32567     },
32568     
32569     generateRules : function(cm){
32570         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
32571         Roo.util.CSS.removeStyleSheet(rulesId);
32572         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
32573             var cid = cm.getColumnId(i);
32574             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
32575                          this.tdSelector, cid, " {\n}\n",
32576                          this.hdSelector, cid, " {\n}\n",
32577                          this.splitSelector, cid, " {\n}\n");
32578         }
32579         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
32580     }
32581 });/*
32582  * Based on:
32583  * Ext JS Library 1.1.1
32584  * Copyright(c) 2006-2007, Ext JS, LLC.
32585  *
32586  * Originally Released Under LGPL - original licence link has changed is not relivant.
32587  *
32588  * Fork - LGPL
32589  * <script type="text/javascript">
32590  */
32591
32592 // private
32593 // This is a support class used internally by the Grid components
32594 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
32595     this.grid = grid;
32596     this.view = grid.getView();
32597     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
32598     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
32599     if(hd2){
32600         this.setHandleElId(Roo.id(hd));
32601         this.setOuterHandleElId(Roo.id(hd2));
32602     }
32603     this.scroll = false;
32604 };
32605 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
32606     maxDragWidth: 120,
32607     getDragData : function(e){
32608         var t = Roo.lib.Event.getTarget(e);
32609         var h = this.view.findHeaderCell(t);
32610         if(h){
32611             return {ddel: h.firstChild, header:h};
32612         }
32613         return false;
32614     },
32615
32616     onInitDrag : function(e){
32617         this.view.headersDisabled = true;
32618         var clone = this.dragData.ddel.cloneNode(true);
32619         clone.id = Roo.id();
32620         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
32621         this.proxy.update(clone);
32622         return true;
32623     },
32624
32625     afterValidDrop : function(){
32626         var v = this.view;
32627         setTimeout(function(){
32628             v.headersDisabled = false;
32629         }, 50);
32630     },
32631
32632     afterInvalidDrop : function(){
32633         var v = this.view;
32634         setTimeout(function(){
32635             v.headersDisabled = false;
32636         }, 50);
32637     }
32638 });
32639 /*
32640  * Based on:
32641  * Ext JS Library 1.1.1
32642  * Copyright(c) 2006-2007, Ext JS, LLC.
32643  *
32644  * Originally Released Under LGPL - original licence link has changed is not relivant.
32645  *
32646  * Fork - LGPL
32647  * <script type="text/javascript">
32648  */
32649 // private
32650 // This is a support class used internally by the Grid components
32651 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
32652     this.grid = grid;
32653     this.view = grid.getView();
32654     // split the proxies so they don't interfere with mouse events
32655     this.proxyTop = Roo.DomHelper.append(document.body, {
32656         cls:"col-move-top", html:"&#160;"
32657     }, true);
32658     this.proxyBottom = Roo.DomHelper.append(document.body, {
32659         cls:"col-move-bottom", html:"&#160;"
32660     }, true);
32661     this.proxyTop.hide = this.proxyBottom.hide = function(){
32662         this.setLeftTop(-100,-100);
32663         this.setStyle("visibility", "hidden");
32664     };
32665     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
32666     // temporarily disabled
32667     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
32668     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
32669 };
32670 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
32671     proxyOffsets : [-4, -9],
32672     fly: Roo.Element.fly,
32673
32674     getTargetFromEvent : function(e){
32675         var t = Roo.lib.Event.getTarget(e);
32676         var cindex = this.view.findCellIndex(t);
32677         if(cindex !== false){
32678             return this.view.getHeaderCell(cindex);
32679         }
32680         return null;
32681     },
32682
32683     nextVisible : function(h){
32684         var v = this.view, cm = this.grid.colModel;
32685         h = h.nextSibling;
32686         while(h){
32687             if(!cm.isHidden(v.getCellIndex(h))){
32688                 return h;
32689             }
32690             h = h.nextSibling;
32691         }
32692         return null;
32693     },
32694
32695     prevVisible : function(h){
32696         var v = this.view, cm = this.grid.colModel;
32697         h = h.prevSibling;
32698         while(h){
32699             if(!cm.isHidden(v.getCellIndex(h))){
32700                 return h;
32701             }
32702             h = h.prevSibling;
32703         }
32704         return null;
32705     },
32706
32707     positionIndicator : function(h, n, e){
32708         var x = Roo.lib.Event.getPageX(e);
32709         var r = Roo.lib.Dom.getRegion(n.firstChild);
32710         var px, pt, py = r.top + this.proxyOffsets[1];
32711         if((r.right - x) <= (r.right-r.left)/2){
32712             px = r.right+this.view.borderWidth;
32713             pt = "after";
32714         }else{
32715             px = r.left;
32716             pt = "before";
32717         }
32718         var oldIndex = this.view.getCellIndex(h);
32719         var newIndex = this.view.getCellIndex(n);
32720
32721         if(this.grid.colModel.isFixed(newIndex)){
32722             return false;
32723         }
32724
32725         var locked = this.grid.colModel.isLocked(newIndex);
32726
32727         if(pt == "after"){
32728             newIndex++;
32729         }
32730         if(oldIndex < newIndex){
32731             newIndex--;
32732         }
32733         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
32734             return false;
32735         }
32736         px +=  this.proxyOffsets[0];
32737         this.proxyTop.setLeftTop(px, py);
32738         this.proxyTop.show();
32739         if(!this.bottomOffset){
32740             this.bottomOffset = this.view.mainHd.getHeight();
32741         }
32742         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
32743         this.proxyBottom.show();
32744         return pt;
32745     },
32746
32747     onNodeEnter : function(n, dd, e, data){
32748         if(data.header != n){
32749             this.positionIndicator(data.header, n, e);
32750         }
32751     },
32752
32753     onNodeOver : function(n, dd, e, data){
32754         var result = false;
32755         if(data.header != n){
32756             result = this.positionIndicator(data.header, n, e);
32757         }
32758         if(!result){
32759             this.proxyTop.hide();
32760             this.proxyBottom.hide();
32761         }
32762         return result ? this.dropAllowed : this.dropNotAllowed;
32763     },
32764
32765     onNodeOut : function(n, dd, e, data){
32766         this.proxyTop.hide();
32767         this.proxyBottom.hide();
32768     },
32769
32770     onNodeDrop : function(n, dd, e, data){
32771         var h = data.header;
32772         if(h != n){
32773             var cm = this.grid.colModel;
32774             var x = Roo.lib.Event.getPageX(e);
32775             var r = Roo.lib.Dom.getRegion(n.firstChild);
32776             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
32777             var oldIndex = this.view.getCellIndex(h);
32778             var newIndex = this.view.getCellIndex(n);
32779             var locked = cm.isLocked(newIndex);
32780             if(pt == "after"){
32781                 newIndex++;
32782             }
32783             if(oldIndex < newIndex){
32784                 newIndex--;
32785             }
32786             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
32787                 return false;
32788             }
32789             cm.setLocked(oldIndex, locked, true);
32790             cm.moveColumn(oldIndex, newIndex);
32791             this.grid.fireEvent("columnmove", oldIndex, newIndex);
32792             return true;
32793         }
32794         return false;
32795     }
32796 });
32797 /*
32798  * Based on:
32799  * Ext JS Library 1.1.1
32800  * Copyright(c) 2006-2007, Ext JS, LLC.
32801  *
32802  * Originally Released Under LGPL - original licence link has changed is not relivant.
32803  *
32804  * Fork - LGPL
32805  * <script type="text/javascript">
32806  */
32807   
32808 /**
32809  * @class Roo.grid.GridView
32810  * @extends Roo.util.Observable
32811  *
32812  * @constructor
32813  * @param {Object} config
32814  */
32815 Roo.grid.GridView = function(config){
32816     Roo.grid.GridView.superclass.constructor.call(this);
32817     this.el = null;
32818
32819     Roo.apply(this, config);
32820 };
32821
32822 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
32823
32824     unselectable :  'unselectable="on"',
32825     unselectableCls :  'x-unselectable',
32826     
32827     
32828     rowClass : "x-grid-row",
32829
32830     cellClass : "x-grid-col",
32831
32832     tdClass : "x-grid-td",
32833
32834     hdClass : "x-grid-hd",
32835
32836     splitClass : "x-grid-split",
32837
32838     sortClasses : ["sort-asc", "sort-desc"],
32839
32840     enableMoveAnim : false,
32841
32842     hlColor: "C3DAF9",
32843
32844     dh : Roo.DomHelper,
32845
32846     fly : Roo.Element.fly,
32847
32848     css : Roo.util.CSS,
32849
32850     borderWidth: 1,
32851
32852     splitOffset: 3,
32853
32854     scrollIncrement : 22,
32855
32856     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
32857
32858     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
32859
32860     bind : function(ds, cm){
32861         if(this.ds){
32862             this.ds.un("load", this.onLoad, this);
32863             this.ds.un("datachanged", this.onDataChange, this);
32864             this.ds.un("add", this.onAdd, this);
32865             this.ds.un("remove", this.onRemove, this);
32866             this.ds.un("update", this.onUpdate, this);
32867             this.ds.un("clear", this.onClear, this);
32868         }
32869         if(ds){
32870             ds.on("load", this.onLoad, this);
32871             ds.on("datachanged", this.onDataChange, this);
32872             ds.on("add", this.onAdd, this);
32873             ds.on("remove", this.onRemove, this);
32874             ds.on("update", this.onUpdate, this);
32875             ds.on("clear", this.onClear, this);
32876         }
32877         this.ds = ds;
32878
32879         if(this.cm){
32880             this.cm.un("widthchange", this.onColWidthChange, this);
32881             this.cm.un("headerchange", this.onHeaderChange, this);
32882             this.cm.un("hiddenchange", this.onHiddenChange, this);
32883             this.cm.un("columnmoved", this.onColumnMove, this);
32884             this.cm.un("columnlockchange", this.onColumnLock, this);
32885         }
32886         if(cm){
32887             this.generateRules(cm);
32888             cm.on("widthchange", this.onColWidthChange, this);
32889             cm.on("headerchange", this.onHeaderChange, this);
32890             cm.on("hiddenchange", this.onHiddenChange, this);
32891             cm.on("columnmoved", this.onColumnMove, this);
32892             cm.on("columnlockchange", this.onColumnLock, this);
32893         }
32894         this.cm = cm;
32895     },
32896
32897     init: function(grid){
32898         Roo.grid.GridView.superclass.init.call(this, grid);
32899
32900         this.bind(grid.dataSource, grid.colModel);
32901
32902         grid.on("headerclick", this.handleHeaderClick, this);
32903
32904         if(grid.trackMouseOver){
32905             grid.on("mouseover", this.onRowOver, this);
32906             grid.on("mouseout", this.onRowOut, this);
32907         }
32908         grid.cancelTextSelection = function(){};
32909         this.gridId = grid.id;
32910
32911         var tpls = this.templates || {};
32912
32913         if(!tpls.master){
32914             tpls.master = new Roo.Template(
32915                '<div class="x-grid" hidefocus="true">',
32916                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
32917                   '<div class="x-grid-topbar"></div>',
32918                   '<div class="x-grid-scroller"><div></div></div>',
32919                   '<div class="x-grid-locked">',
32920                       '<div class="x-grid-header">{lockedHeader}</div>',
32921                       '<div class="x-grid-body">{lockedBody}</div>',
32922                   "</div>",
32923                   '<div class="x-grid-viewport">',
32924                       '<div class="x-grid-header">{header}</div>',
32925                       '<div class="x-grid-body">{body}</div>',
32926                   "</div>",
32927                   '<div class="x-grid-bottombar"></div>',
32928                  
32929                   '<div class="x-grid-resize-proxy">&#160;</div>',
32930                "</div>"
32931             );
32932             tpls.master.disableformats = true;
32933         }
32934
32935         if(!tpls.header){
32936             tpls.header = new Roo.Template(
32937                '<table border="0" cellspacing="0" cellpadding="0">',
32938                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
32939                "</table>{splits}"
32940             );
32941             tpls.header.disableformats = true;
32942         }
32943         tpls.header.compile();
32944
32945         if(!tpls.hcell){
32946             tpls.hcell = new Roo.Template(
32947                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
32948                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
32949                 "</div></td>"
32950              );
32951              tpls.hcell.disableFormats = true;
32952         }
32953         tpls.hcell.compile();
32954
32955         if(!tpls.hsplit){
32956             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
32957                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
32958             tpls.hsplit.disableFormats = true;
32959         }
32960         tpls.hsplit.compile();
32961
32962         if(!tpls.body){
32963             tpls.body = new Roo.Template(
32964                '<table border="0" cellspacing="0" cellpadding="0">',
32965                "<tbody>{rows}</tbody>",
32966                "</table>"
32967             );
32968             tpls.body.disableFormats = true;
32969         }
32970         tpls.body.compile();
32971
32972         if(!tpls.row){
32973             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
32974             tpls.row.disableFormats = true;
32975         }
32976         tpls.row.compile();
32977
32978         if(!tpls.cell){
32979             tpls.cell = new Roo.Template(
32980                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
32981                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
32982                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
32983                 "</td>"
32984             );
32985             tpls.cell.disableFormats = true;
32986         }
32987         tpls.cell.compile();
32988
32989         this.templates = tpls;
32990     },
32991
32992     // remap these for backwards compat
32993     onColWidthChange : function(){
32994         this.updateColumns.apply(this, arguments);
32995     },
32996     onHeaderChange : function(){
32997         this.updateHeaders.apply(this, arguments);
32998     }, 
32999     onHiddenChange : function(){
33000         this.handleHiddenChange.apply(this, arguments);
33001     },
33002     onColumnMove : function(){
33003         this.handleColumnMove.apply(this, arguments);
33004     },
33005     onColumnLock : function(){
33006         this.handleLockChange.apply(this, arguments);
33007     },
33008
33009     onDataChange : function(){
33010         this.refresh();
33011         this.updateHeaderSortState();
33012     },
33013
33014     onClear : function(){
33015         this.refresh();
33016     },
33017
33018     onUpdate : function(ds, record){
33019         this.refreshRow(record);
33020     },
33021
33022     refreshRow : function(record){
33023         var ds = this.ds, index;
33024         if(typeof record == 'number'){
33025             index = record;
33026             record = ds.getAt(index);
33027         }else{
33028             index = ds.indexOf(record);
33029         }
33030         this.insertRows(ds, index, index, true);
33031         this.onRemove(ds, record, index+1, true);
33032         this.syncRowHeights(index, index);
33033         this.layout();
33034         this.fireEvent("rowupdated", this, index, record);
33035     },
33036
33037     onAdd : function(ds, records, index){
33038         this.insertRows(ds, index, index + (records.length-1));
33039     },
33040
33041     onRemove : function(ds, record, index, isUpdate){
33042         if(isUpdate !== true){
33043             this.fireEvent("beforerowremoved", this, index, record);
33044         }
33045         var bt = this.getBodyTable(), lt = this.getLockedTable();
33046         if(bt.rows[index]){
33047             bt.firstChild.removeChild(bt.rows[index]);
33048         }
33049         if(lt.rows[index]){
33050             lt.firstChild.removeChild(lt.rows[index]);
33051         }
33052         if(isUpdate !== true){
33053             this.stripeRows(index);
33054             this.syncRowHeights(index, index);
33055             this.layout();
33056             this.fireEvent("rowremoved", this, index, record);
33057         }
33058     },
33059
33060     onLoad : function(){
33061         this.scrollToTop();
33062     },
33063
33064     /**
33065      * Scrolls the grid to the top
33066      */
33067     scrollToTop : function(){
33068         if(this.scroller){
33069             this.scroller.dom.scrollTop = 0;
33070             this.syncScroll();
33071         }
33072     },
33073
33074     /**
33075      * Gets a panel in the header of the grid that can be used for toolbars etc.
33076      * After modifying the contents of this panel a call to grid.autoSize() may be
33077      * required to register any changes in size.
33078      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
33079      * @return Roo.Element
33080      */
33081     getHeaderPanel : function(doShow){
33082         if(doShow){
33083             this.headerPanel.show();
33084         }
33085         return this.headerPanel;
33086     },
33087
33088     /**
33089      * Gets a panel in the footer of the grid that can be used for toolbars etc.
33090      * After modifying the contents of this panel a call to grid.autoSize() may be
33091      * required to register any changes in size.
33092      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
33093      * @return Roo.Element
33094      */
33095     getFooterPanel : function(doShow){
33096         if(doShow){
33097             this.footerPanel.show();
33098         }
33099         return this.footerPanel;
33100     },
33101
33102     initElements : function(){
33103         var E = Roo.Element;
33104         var el = this.grid.getGridEl().dom.firstChild;
33105         var cs = el.childNodes;
33106
33107         this.el = new E(el);
33108         
33109          this.focusEl = new E(el.firstChild);
33110         this.focusEl.swallowEvent("click", true);
33111         
33112         this.headerPanel = new E(cs[1]);
33113         this.headerPanel.enableDisplayMode("block");
33114
33115         this.scroller = new E(cs[2]);
33116         this.scrollSizer = new E(this.scroller.dom.firstChild);
33117
33118         this.lockedWrap = new E(cs[3]);
33119         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
33120         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
33121
33122         this.mainWrap = new E(cs[4]);
33123         this.mainHd = new E(this.mainWrap.dom.firstChild);
33124         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
33125
33126         this.footerPanel = new E(cs[5]);
33127         this.footerPanel.enableDisplayMode("block");
33128
33129         this.resizeProxy = new E(cs[6]);
33130
33131         this.headerSelector = String.format(
33132            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
33133            this.lockedHd.id, this.mainHd.id
33134         );
33135
33136         this.splitterSelector = String.format(
33137            '#{0} div.x-grid-split, #{1} div.x-grid-split',
33138            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
33139         );
33140     },
33141     idToCssName : function(s)
33142     {
33143         return s.replace(/[^a-z0-9]+/ig, '-');
33144     },
33145
33146     getHeaderCell : function(index){
33147         return Roo.DomQuery.select(this.headerSelector)[index];
33148     },
33149
33150     getHeaderCellMeasure : function(index){
33151         return this.getHeaderCell(index).firstChild;
33152     },
33153
33154     getHeaderCellText : function(index){
33155         return this.getHeaderCell(index).firstChild.firstChild;
33156     },
33157
33158     getLockedTable : function(){
33159         return this.lockedBody.dom.firstChild;
33160     },
33161
33162     getBodyTable : function(){
33163         return this.mainBody.dom.firstChild;
33164     },
33165
33166     getLockedRow : function(index){
33167         return this.getLockedTable().rows[index];
33168     },
33169
33170     getRow : function(index){
33171         return this.getBodyTable().rows[index];
33172     },
33173
33174     getRowComposite : function(index){
33175         if(!this.rowEl){
33176             this.rowEl = new Roo.CompositeElementLite();
33177         }
33178         var els = [], lrow, mrow;
33179         if(lrow = this.getLockedRow(index)){
33180             els.push(lrow);
33181         }
33182         if(mrow = this.getRow(index)){
33183             els.push(mrow);
33184         }
33185         this.rowEl.elements = els;
33186         return this.rowEl;
33187     },
33188     /**
33189      * Gets the 'td' of the cell
33190      * 
33191      * @param {Integer} rowIndex row to select
33192      * @param {Integer} colIndex column to select
33193      * 
33194      * @return {Object} 
33195      */
33196     getCell : function(rowIndex, colIndex){
33197         var locked = this.cm.getLockedCount();
33198         var source;
33199         if(colIndex < locked){
33200             source = this.lockedBody.dom.firstChild;
33201         }else{
33202             source = this.mainBody.dom.firstChild;
33203             colIndex -= locked;
33204         }
33205         return source.rows[rowIndex].childNodes[colIndex];
33206     },
33207
33208     getCellText : function(rowIndex, colIndex){
33209         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
33210     },
33211
33212     getCellBox : function(cell){
33213         var b = this.fly(cell).getBox();
33214         if(Roo.isOpera){ // opera fails to report the Y
33215             b.y = cell.offsetTop + this.mainBody.getY();
33216         }
33217         return b;
33218     },
33219
33220     getCellIndex : function(cell){
33221         var id = String(cell.className).match(this.cellRE);
33222         if(id){
33223             return parseInt(id[1], 10);
33224         }
33225         return 0;
33226     },
33227
33228     findHeaderIndex : function(n){
33229         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33230         return r ? this.getCellIndex(r) : false;
33231     },
33232
33233     findHeaderCell : function(n){
33234         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33235         return r ? r : false;
33236     },
33237
33238     findRowIndex : function(n){
33239         if(!n){
33240             return false;
33241         }
33242         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
33243         return r ? r.rowIndex : false;
33244     },
33245
33246     findCellIndex : function(node){
33247         var stop = this.el.dom;
33248         while(node && node != stop){
33249             if(this.findRE.test(node.className)){
33250                 return this.getCellIndex(node);
33251             }
33252             node = node.parentNode;
33253         }
33254         return false;
33255     },
33256
33257     getColumnId : function(index){
33258         return this.cm.getColumnId(index);
33259     },
33260
33261     getSplitters : function()
33262     {
33263         if(this.splitterSelector){
33264            return Roo.DomQuery.select(this.splitterSelector);
33265         }else{
33266             return null;
33267       }
33268     },
33269
33270     getSplitter : function(index){
33271         return this.getSplitters()[index];
33272     },
33273
33274     onRowOver : function(e, t){
33275         var row;
33276         if((row = this.findRowIndex(t)) !== false){
33277             this.getRowComposite(row).addClass("x-grid-row-over");
33278         }
33279     },
33280
33281     onRowOut : function(e, t){
33282         var row;
33283         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
33284             this.getRowComposite(row).removeClass("x-grid-row-over");
33285         }
33286     },
33287
33288     renderHeaders : function(){
33289         var cm = this.cm;
33290         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
33291         var cb = [], lb = [], sb = [], lsb = [], p = {};
33292         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33293             p.cellId = "x-grid-hd-0-" + i;
33294             p.splitId = "x-grid-csplit-0-" + i;
33295             p.id = cm.getColumnId(i);
33296             p.value = cm.getColumnHeader(i) || "";
33297             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
33298             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
33299             if(!cm.isLocked(i)){
33300                 cb[cb.length] = ct.apply(p);
33301                 sb[sb.length] = st.apply(p);
33302             }else{
33303                 lb[lb.length] = ct.apply(p);
33304                 lsb[lsb.length] = st.apply(p);
33305             }
33306         }
33307         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
33308                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
33309     },
33310
33311     updateHeaders : function(){
33312         var html = this.renderHeaders();
33313         this.lockedHd.update(html[0]);
33314         this.mainHd.update(html[1]);
33315     },
33316
33317     /**
33318      * Focuses the specified row.
33319      * @param {Number} row The row index
33320      */
33321     focusRow : function(row)
33322     {
33323         //Roo.log('GridView.focusRow');
33324         var x = this.scroller.dom.scrollLeft;
33325         this.focusCell(row, 0, false);
33326         this.scroller.dom.scrollLeft = x;
33327     },
33328
33329     /**
33330      * Focuses the specified cell.
33331      * @param {Number} row The row index
33332      * @param {Number} col The column index
33333      * @param {Boolean} hscroll false to disable horizontal scrolling
33334      */
33335     focusCell : function(row, col, hscroll)
33336     {
33337         //Roo.log('GridView.focusCell');
33338         var el = this.ensureVisible(row, col, hscroll);
33339         this.focusEl.alignTo(el, "tl-tl");
33340         if(Roo.isGecko){
33341             this.focusEl.focus();
33342         }else{
33343             this.focusEl.focus.defer(1, this.focusEl);
33344         }
33345     },
33346
33347     /**
33348      * Scrolls the specified cell into view
33349      * @param {Number} row The row index
33350      * @param {Number} col The column index
33351      * @param {Boolean} hscroll false to disable horizontal scrolling
33352      */
33353     ensureVisible : function(row, col, hscroll)
33354     {
33355         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
33356         //return null; //disable for testing.
33357         if(typeof row != "number"){
33358             row = row.rowIndex;
33359         }
33360         if(row < 0 && row >= this.ds.getCount()){
33361             return  null;
33362         }
33363         col = (col !== undefined ? col : 0);
33364         var cm = this.grid.colModel;
33365         while(cm.isHidden(col)){
33366             col++;
33367         }
33368
33369         var el = this.getCell(row, col);
33370         if(!el){
33371             return null;
33372         }
33373         var c = this.scroller.dom;
33374
33375         var ctop = parseInt(el.offsetTop, 10);
33376         var cleft = parseInt(el.offsetLeft, 10);
33377         var cbot = ctop + el.offsetHeight;
33378         var cright = cleft + el.offsetWidth;
33379         
33380         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
33381         var stop = parseInt(c.scrollTop, 10);
33382         var sleft = parseInt(c.scrollLeft, 10);
33383         var sbot = stop + ch;
33384         var sright = sleft + c.clientWidth;
33385         /*
33386         Roo.log('GridView.ensureVisible:' +
33387                 ' ctop:' + ctop +
33388                 ' c.clientHeight:' + c.clientHeight +
33389                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
33390                 ' stop:' + stop +
33391                 ' cbot:' + cbot +
33392                 ' sbot:' + sbot +
33393                 ' ch:' + ch  
33394                 );
33395         */
33396         if(ctop < stop){
33397              c.scrollTop = ctop;
33398             //Roo.log("set scrolltop to ctop DISABLE?");
33399         }else if(cbot > sbot){
33400             //Roo.log("set scrolltop to cbot-ch");
33401             c.scrollTop = cbot-ch;
33402         }
33403         
33404         if(hscroll !== false){
33405             if(cleft < sleft){
33406                 c.scrollLeft = cleft;
33407             }else if(cright > sright){
33408                 c.scrollLeft = cright-c.clientWidth;
33409             }
33410         }
33411          
33412         return el;
33413     },
33414
33415     updateColumns : function(){
33416         this.grid.stopEditing();
33417         var cm = this.grid.colModel, colIds = this.getColumnIds();
33418         //var totalWidth = cm.getTotalWidth();
33419         var pos = 0;
33420         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33421             //if(cm.isHidden(i)) continue;
33422             var w = cm.getColumnWidth(i);
33423             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33424             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33425         }
33426         this.updateSplitters();
33427     },
33428
33429     generateRules : function(cm){
33430         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
33431         Roo.util.CSS.removeStyleSheet(rulesId);
33432         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33433             var cid = cm.getColumnId(i);
33434             var align = '';
33435             if(cm.config[i].align){
33436                 align = 'text-align:'+cm.config[i].align+';';
33437             }
33438             var hidden = '';
33439             if(cm.isHidden(i)){
33440                 hidden = 'display:none;';
33441             }
33442             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
33443             ruleBuf.push(
33444                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
33445                     this.hdSelector, cid, " {\n", align, width, "}\n",
33446                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
33447                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
33448         }
33449         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33450     },
33451
33452     updateSplitters : function(){
33453         var cm = this.cm, s = this.getSplitters();
33454         if(s){ // splitters not created yet
33455             var pos = 0, locked = true;
33456             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33457                 if(cm.isHidden(i)) {
33458                     continue;
33459                 }
33460                 var w = cm.getColumnWidth(i); // make sure it's a number
33461                 if(!cm.isLocked(i) && locked){
33462                     pos = 0;
33463                     locked = false;
33464                 }
33465                 pos += w;
33466                 s[i].style.left = (pos-this.splitOffset) + "px";
33467             }
33468         }
33469     },
33470
33471     handleHiddenChange : function(colModel, colIndex, hidden){
33472         if(hidden){
33473             this.hideColumn(colIndex);
33474         }else{
33475             this.unhideColumn(colIndex);
33476         }
33477     },
33478
33479     hideColumn : function(colIndex){
33480         var cid = this.getColumnId(colIndex);
33481         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
33482         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
33483         if(Roo.isSafari){
33484             this.updateHeaders();
33485         }
33486         this.updateSplitters();
33487         this.layout();
33488     },
33489
33490     unhideColumn : function(colIndex){
33491         var cid = this.getColumnId(colIndex);
33492         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
33493         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
33494
33495         if(Roo.isSafari){
33496             this.updateHeaders();
33497         }
33498         this.updateSplitters();
33499         this.layout();
33500     },
33501
33502     insertRows : function(dm, firstRow, lastRow, isUpdate){
33503         if(firstRow == 0 && lastRow == dm.getCount()-1){
33504             this.refresh();
33505         }else{
33506             if(!isUpdate){
33507                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
33508             }
33509             var s = this.getScrollState();
33510             var markup = this.renderRows(firstRow, lastRow);
33511             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
33512             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
33513             this.restoreScroll(s);
33514             if(!isUpdate){
33515                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
33516                 this.syncRowHeights(firstRow, lastRow);
33517                 this.stripeRows(firstRow);
33518                 this.layout();
33519             }
33520         }
33521     },
33522
33523     bufferRows : function(markup, target, index){
33524         var before = null, trows = target.rows, tbody = target.tBodies[0];
33525         if(index < trows.length){
33526             before = trows[index];
33527         }
33528         var b = document.createElement("div");
33529         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
33530         var rows = b.firstChild.rows;
33531         for(var i = 0, len = rows.length; i < len; i++){
33532             if(before){
33533                 tbody.insertBefore(rows[0], before);
33534             }else{
33535                 tbody.appendChild(rows[0]);
33536             }
33537         }
33538         b.innerHTML = "";
33539         b = null;
33540     },
33541
33542     deleteRows : function(dm, firstRow, lastRow){
33543         if(dm.getRowCount()<1){
33544             this.fireEvent("beforerefresh", this);
33545             this.mainBody.update("");
33546             this.lockedBody.update("");
33547             this.fireEvent("refresh", this);
33548         }else{
33549             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
33550             var bt = this.getBodyTable();
33551             var tbody = bt.firstChild;
33552             var rows = bt.rows;
33553             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
33554                 tbody.removeChild(rows[firstRow]);
33555             }
33556             this.stripeRows(firstRow);
33557             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
33558         }
33559     },
33560
33561     updateRows : function(dataSource, firstRow, lastRow){
33562         var s = this.getScrollState();
33563         this.refresh();
33564         this.restoreScroll(s);
33565     },
33566
33567     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
33568         if(!noRefresh){
33569            this.refresh();
33570         }
33571         this.updateHeaderSortState();
33572     },
33573
33574     getScrollState : function(){
33575         
33576         var sb = this.scroller.dom;
33577         return {left: sb.scrollLeft, top: sb.scrollTop};
33578     },
33579
33580     stripeRows : function(startRow){
33581         if(!this.grid.stripeRows || this.ds.getCount() < 1){
33582             return;
33583         }
33584         startRow = startRow || 0;
33585         var rows = this.getBodyTable().rows;
33586         var lrows = this.getLockedTable().rows;
33587         var cls = ' x-grid-row-alt ';
33588         for(var i = startRow, len = rows.length; i < len; i++){
33589             var row = rows[i], lrow = lrows[i];
33590             var isAlt = ((i+1) % 2 == 0);
33591             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
33592             if(isAlt == hasAlt){
33593                 continue;
33594             }
33595             if(isAlt){
33596                 row.className += " x-grid-row-alt";
33597             }else{
33598                 row.className = row.className.replace("x-grid-row-alt", "");
33599             }
33600             if(lrow){
33601                 lrow.className = row.className;
33602             }
33603         }
33604     },
33605
33606     restoreScroll : function(state){
33607         //Roo.log('GridView.restoreScroll');
33608         var sb = this.scroller.dom;
33609         sb.scrollLeft = state.left;
33610         sb.scrollTop = state.top;
33611         this.syncScroll();
33612     },
33613
33614     syncScroll : function(){
33615         //Roo.log('GridView.syncScroll');
33616         var sb = this.scroller.dom;
33617         var sh = this.mainHd.dom;
33618         var bs = this.mainBody.dom;
33619         var lv = this.lockedBody.dom;
33620         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
33621         lv.scrollTop = bs.scrollTop = sb.scrollTop;
33622     },
33623
33624     handleScroll : function(e){
33625         this.syncScroll();
33626         var sb = this.scroller.dom;
33627         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
33628         e.stopEvent();
33629     },
33630
33631     handleWheel : function(e){
33632         var d = e.getWheelDelta();
33633         this.scroller.dom.scrollTop -= d*22;
33634         // set this here to prevent jumpy scrolling on large tables
33635         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
33636         e.stopEvent();
33637     },
33638
33639     renderRows : function(startRow, endRow){
33640         // pull in all the crap needed to render rows
33641         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
33642         var colCount = cm.getColumnCount();
33643
33644         if(ds.getCount() < 1){
33645             return ["", ""];
33646         }
33647
33648         // build a map for all the columns
33649         var cs = [];
33650         for(var i = 0; i < colCount; i++){
33651             var name = cm.getDataIndex(i);
33652             cs[i] = {
33653                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
33654                 renderer : cm.getRenderer(i),
33655                 id : cm.getColumnId(i),
33656                 locked : cm.isLocked(i),
33657                 has_editor : cm.isCellEditable(i)
33658             };
33659         }
33660
33661         startRow = startRow || 0;
33662         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
33663
33664         // records to render
33665         var rs = ds.getRange(startRow, endRow);
33666
33667         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
33668     },
33669
33670     // As much as I hate to duplicate code, this was branched because FireFox really hates
33671     // [].join("") on strings. The performance difference was substantial enough to
33672     // branch this function
33673     doRender : Roo.isGecko ?
33674             function(cs, rs, ds, startRow, colCount, stripe){
33675                 var ts = this.templates, ct = ts.cell, rt = ts.row;
33676                 // buffers
33677                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
33678                 
33679                 var hasListener = this.grid.hasListener('rowclass');
33680                 var rowcfg = {};
33681                 for(var j = 0, len = rs.length; j < len; j++){
33682                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
33683                     for(var i = 0; i < colCount; i++){
33684                         c = cs[i];
33685                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
33686                         p.id = c.id;
33687                         p.css = p.attr = "";
33688                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
33689                         if(p.value == undefined || p.value === "") {
33690                             p.value = "&#160;";
33691                         }
33692                         if(c.has_editor){
33693                             p.css += ' x-grid-editable-cell';
33694                         }
33695                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
33696                             p.css +=  ' x-grid-dirty-cell';
33697                         }
33698                         var markup = ct.apply(p);
33699                         if(!c.locked){
33700                             cb+= markup;
33701                         }else{
33702                             lcb+= markup;
33703                         }
33704                     }
33705                     var alt = [];
33706                     if(stripe && ((rowIndex+1) % 2 == 0)){
33707                         alt.push("x-grid-row-alt")
33708                     }
33709                     if(r.dirty){
33710                         alt.push(  " x-grid-dirty-row");
33711                     }
33712                     rp.cells = lcb;
33713                     if(this.getRowClass){
33714                         alt.push(this.getRowClass(r, rowIndex));
33715                     }
33716                     if (hasListener) {
33717                         rowcfg = {
33718                              
33719                             record: r,
33720                             rowIndex : rowIndex,
33721                             rowClass : ''
33722                         };
33723                         this.grid.fireEvent('rowclass', this, rowcfg);
33724                         alt.push(rowcfg.rowClass);
33725                     }
33726                     rp.alt = alt.join(" ");
33727                     lbuf+= rt.apply(rp);
33728                     rp.cells = cb;
33729                     buf+=  rt.apply(rp);
33730                 }
33731                 return [lbuf, buf];
33732             } :
33733             function(cs, rs, ds, startRow, colCount, stripe){
33734                 var ts = this.templates, ct = ts.cell, rt = ts.row;
33735                 // buffers
33736                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
33737                 var hasListener = this.grid.hasListener('rowclass');
33738  
33739                 var rowcfg = {};
33740                 for(var j = 0, len = rs.length; j < len; j++){
33741                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
33742                     for(var i = 0; i < colCount; i++){
33743                         c = cs[i];
33744                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
33745                         p.id = c.id;
33746                         p.css = p.attr = "";
33747                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
33748                         if(p.value == undefined || p.value === "") {
33749                             p.value = "&#160;";
33750                         }
33751                         //Roo.log(c);
33752                          if(c.has_editor){
33753                             p.css += ' x-grid-editable-cell';
33754                         }
33755                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
33756                             p.css += ' x-grid-dirty-cell' 
33757                         }
33758                         
33759                         var markup = ct.apply(p);
33760                         if(!c.locked){
33761                             cb[cb.length] = markup;
33762                         }else{
33763                             lcb[lcb.length] = markup;
33764                         }
33765                     }
33766                     var alt = [];
33767                     if(stripe && ((rowIndex+1) % 2 == 0)){
33768                         alt.push( "x-grid-row-alt");
33769                     }
33770                     if(r.dirty){
33771                         alt.push(" x-grid-dirty-row");
33772                     }
33773                     rp.cells = lcb;
33774                     if(this.getRowClass){
33775                         alt.push( this.getRowClass(r, rowIndex));
33776                     }
33777                     if (hasListener) {
33778                         rowcfg = {
33779                              
33780                             record: r,
33781                             rowIndex : rowIndex,
33782                             rowClass : ''
33783                         };
33784                         this.grid.fireEvent('rowclass', this, rowcfg);
33785                         alt.push(rowcfg.rowClass);
33786                     }
33787                     
33788                     rp.alt = alt.join(" ");
33789                     rp.cells = lcb.join("");
33790                     lbuf[lbuf.length] = rt.apply(rp);
33791                     rp.cells = cb.join("");
33792                     buf[buf.length] =  rt.apply(rp);
33793                 }
33794                 return [lbuf.join(""), buf.join("")];
33795             },
33796
33797     renderBody : function(){
33798         var markup = this.renderRows();
33799         var bt = this.templates.body;
33800         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
33801     },
33802
33803     /**
33804      * Refreshes the grid
33805      * @param {Boolean} headersToo
33806      */
33807     refresh : function(headersToo){
33808         this.fireEvent("beforerefresh", this);
33809         this.grid.stopEditing();
33810         var result = this.renderBody();
33811         this.lockedBody.update(result[0]);
33812         this.mainBody.update(result[1]);
33813         if(headersToo === true){
33814             this.updateHeaders();
33815             this.updateColumns();
33816             this.updateSplitters();
33817             this.updateHeaderSortState();
33818         }
33819         this.syncRowHeights();
33820         this.layout();
33821         this.fireEvent("refresh", this);
33822     },
33823
33824     handleColumnMove : function(cm, oldIndex, newIndex){
33825         this.indexMap = null;
33826         var s = this.getScrollState();
33827         this.refresh(true);
33828         this.restoreScroll(s);
33829         this.afterMove(newIndex);
33830     },
33831
33832     afterMove : function(colIndex){
33833         if(this.enableMoveAnim && Roo.enableFx){
33834             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
33835         }
33836         // if multisort - fix sortOrder, and reload..
33837         if (this.grid.dataSource.multiSort) {
33838             // the we can call sort again..
33839             var dm = this.grid.dataSource;
33840             var cm = this.grid.colModel;
33841             var so = [];
33842             for(var i = 0; i < cm.config.length; i++ ) {
33843                 
33844                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
33845                     continue; // dont' bother, it's not in sort list or being set.
33846                 }
33847                 
33848                 so.push(cm.config[i].dataIndex);
33849             };
33850             dm.sortOrder = so;
33851             dm.load(dm.lastOptions);
33852             
33853             
33854         }
33855         
33856     },
33857
33858     updateCell : function(dm, rowIndex, dataIndex){
33859         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
33860         if(typeof colIndex == "undefined"){ // not present in grid
33861             return;
33862         }
33863         var cm = this.grid.colModel;
33864         var cell = this.getCell(rowIndex, colIndex);
33865         var cellText = this.getCellText(rowIndex, colIndex);
33866
33867         var p = {
33868             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
33869             id : cm.getColumnId(colIndex),
33870             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
33871         };
33872         var renderer = cm.getRenderer(colIndex);
33873         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
33874         if(typeof val == "undefined" || val === "") {
33875             val = "&#160;";
33876         }
33877         cellText.innerHTML = val;
33878         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
33879         this.syncRowHeights(rowIndex, rowIndex);
33880     },
33881
33882     calcColumnWidth : function(colIndex, maxRowsToMeasure){
33883         var maxWidth = 0;
33884         if(this.grid.autoSizeHeaders){
33885             var h = this.getHeaderCellMeasure(colIndex);
33886             maxWidth = Math.max(maxWidth, h.scrollWidth);
33887         }
33888         var tb, index;
33889         if(this.cm.isLocked(colIndex)){
33890             tb = this.getLockedTable();
33891             index = colIndex;
33892         }else{
33893             tb = this.getBodyTable();
33894             index = colIndex - this.cm.getLockedCount();
33895         }
33896         if(tb && tb.rows){
33897             var rows = tb.rows;
33898             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
33899             for(var i = 0; i < stopIndex; i++){
33900                 var cell = rows[i].childNodes[index].firstChild;
33901                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
33902             }
33903         }
33904         return maxWidth + /*margin for error in IE*/ 5;
33905     },
33906     /**
33907      * Autofit a column to its content.
33908      * @param {Number} colIndex
33909      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
33910      */
33911      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
33912          if(this.cm.isHidden(colIndex)){
33913              return; // can't calc a hidden column
33914          }
33915         if(forceMinSize){
33916             var cid = this.cm.getColumnId(colIndex);
33917             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
33918            if(this.grid.autoSizeHeaders){
33919                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
33920            }
33921         }
33922         var newWidth = this.calcColumnWidth(colIndex);
33923         this.cm.setColumnWidth(colIndex,
33924             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
33925         if(!suppressEvent){
33926             this.grid.fireEvent("columnresize", colIndex, newWidth);
33927         }
33928     },
33929
33930     /**
33931      * Autofits all columns to their content and then expands to fit any extra space in the grid
33932      */
33933      autoSizeColumns : function(){
33934         var cm = this.grid.colModel;
33935         var colCount = cm.getColumnCount();
33936         for(var i = 0; i < colCount; i++){
33937             this.autoSizeColumn(i, true, true);
33938         }
33939         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
33940             this.fitColumns();
33941         }else{
33942             this.updateColumns();
33943             this.layout();
33944         }
33945     },
33946
33947     /**
33948      * Autofits all columns to the grid's width proportionate with their current size
33949      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
33950      */
33951     fitColumns : function(reserveScrollSpace){
33952         var cm = this.grid.colModel;
33953         var colCount = cm.getColumnCount();
33954         var cols = [];
33955         var width = 0;
33956         var i, w;
33957         for (i = 0; i < colCount; i++){
33958             if(!cm.isHidden(i) && !cm.isFixed(i)){
33959                 w = cm.getColumnWidth(i);
33960                 cols.push(i);
33961                 cols.push(w);
33962                 width += w;
33963             }
33964         }
33965         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
33966         if(reserveScrollSpace){
33967             avail -= 17;
33968         }
33969         var frac = (avail - cm.getTotalWidth())/width;
33970         while (cols.length){
33971             w = cols.pop();
33972             i = cols.pop();
33973             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
33974         }
33975         this.updateColumns();
33976         this.layout();
33977     },
33978
33979     onRowSelect : function(rowIndex){
33980         var row = this.getRowComposite(rowIndex);
33981         row.addClass("x-grid-row-selected");
33982     },
33983
33984     onRowDeselect : function(rowIndex){
33985         var row = this.getRowComposite(rowIndex);
33986         row.removeClass("x-grid-row-selected");
33987     },
33988
33989     onCellSelect : function(row, col){
33990         var cell = this.getCell(row, col);
33991         if(cell){
33992             Roo.fly(cell).addClass("x-grid-cell-selected");
33993         }
33994     },
33995
33996     onCellDeselect : function(row, col){
33997         var cell = this.getCell(row, col);
33998         if(cell){
33999             Roo.fly(cell).removeClass("x-grid-cell-selected");
34000         }
34001     },
34002
34003     updateHeaderSortState : function(){
34004         
34005         // sort state can be single { field: xxx, direction : yyy}
34006         // or   { xxx=>ASC , yyy : DESC ..... }
34007         
34008         var mstate = {};
34009         if (!this.ds.multiSort) { 
34010             var state = this.ds.getSortState();
34011             if(!state){
34012                 return;
34013             }
34014             mstate[state.field] = state.direction;
34015             // FIXME... - this is not used here.. but might be elsewhere..
34016             this.sortState = state;
34017             
34018         } else {
34019             mstate = this.ds.sortToggle;
34020         }
34021         //remove existing sort classes..
34022         
34023         var sc = this.sortClasses;
34024         var hds = this.el.select(this.headerSelector).removeClass(sc);
34025         
34026         for(var f in mstate) {
34027         
34028             var sortColumn = this.cm.findColumnIndex(f);
34029             
34030             if(sortColumn != -1){
34031                 var sortDir = mstate[f];        
34032                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
34033             }
34034         }
34035         
34036          
34037         
34038     },
34039
34040
34041     handleHeaderClick : function(g, index,e){
34042         
34043         Roo.log("header click");
34044         
34045         if (Roo.isTouch) {
34046             // touch events on header are handled by context
34047             this.handleHdCtx(g,index,e);
34048             return;
34049         }
34050         
34051         
34052         if(this.headersDisabled){
34053             return;
34054         }
34055         var dm = g.dataSource, cm = g.colModel;
34056         if(!cm.isSortable(index)){
34057             return;
34058         }
34059         g.stopEditing();
34060         
34061         if (dm.multiSort) {
34062             // update the sortOrder
34063             var so = [];
34064             for(var i = 0; i < cm.config.length; i++ ) {
34065                 
34066                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
34067                     continue; // dont' bother, it's not in sort list or being set.
34068                 }
34069                 
34070                 so.push(cm.config[i].dataIndex);
34071             };
34072             dm.sortOrder = so;
34073         }
34074         
34075         
34076         dm.sort(cm.getDataIndex(index));
34077     },
34078
34079
34080     destroy : function(){
34081         if(this.colMenu){
34082             this.colMenu.removeAll();
34083             Roo.menu.MenuMgr.unregister(this.colMenu);
34084             this.colMenu.getEl().remove();
34085             delete this.colMenu;
34086         }
34087         if(this.hmenu){
34088             this.hmenu.removeAll();
34089             Roo.menu.MenuMgr.unregister(this.hmenu);
34090             this.hmenu.getEl().remove();
34091             delete this.hmenu;
34092         }
34093         if(this.grid.enableColumnMove){
34094             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34095             if(dds){
34096                 for(var dd in dds){
34097                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
34098                         var elid = dds[dd].dragElId;
34099                         dds[dd].unreg();
34100                         Roo.get(elid).remove();
34101                     } else if(dds[dd].config.isTarget){
34102                         dds[dd].proxyTop.remove();
34103                         dds[dd].proxyBottom.remove();
34104                         dds[dd].unreg();
34105                     }
34106                     if(Roo.dd.DDM.locationCache[dd]){
34107                         delete Roo.dd.DDM.locationCache[dd];
34108                     }
34109                 }
34110                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34111             }
34112         }
34113         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
34114         this.bind(null, null);
34115         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
34116     },
34117
34118     handleLockChange : function(){
34119         this.refresh(true);
34120     },
34121
34122     onDenyColumnLock : function(){
34123
34124     },
34125
34126     onDenyColumnHide : function(){
34127
34128     },
34129
34130     handleHdMenuClick : function(item){
34131         var index = this.hdCtxIndex;
34132         var cm = this.cm, ds = this.ds;
34133         switch(item.id){
34134             case "asc":
34135                 ds.sort(cm.getDataIndex(index), "ASC");
34136                 break;
34137             case "desc":
34138                 ds.sort(cm.getDataIndex(index), "DESC");
34139                 break;
34140             case "lock":
34141                 var lc = cm.getLockedCount();
34142                 if(cm.getColumnCount(true) <= lc+1){
34143                     this.onDenyColumnLock();
34144                     return;
34145                 }
34146                 if(lc != index){
34147                     cm.setLocked(index, true, true);
34148                     cm.moveColumn(index, lc);
34149                     this.grid.fireEvent("columnmove", index, lc);
34150                 }else{
34151                     cm.setLocked(index, true);
34152                 }
34153             break;
34154             case "unlock":
34155                 var lc = cm.getLockedCount();
34156                 if((lc-1) != index){
34157                     cm.setLocked(index, false, true);
34158                     cm.moveColumn(index, lc-1);
34159                     this.grid.fireEvent("columnmove", index, lc-1);
34160                 }else{
34161                     cm.setLocked(index, false);
34162                 }
34163             break;
34164             case 'wider': // used to expand cols on touch..
34165             case 'narrow':
34166                 var cw = cm.getColumnWidth(index);
34167                 cw += (item.id == 'wider' ? 1 : -1) * 50;
34168                 cw = Math.max(0, cw);
34169                 cw = Math.min(cw,4000);
34170                 cm.setColumnWidth(index, cw);
34171                 break;
34172                 
34173             default:
34174                 index = cm.getIndexById(item.id.substr(4));
34175                 if(index != -1){
34176                     if(item.checked && cm.getColumnCount(true) <= 1){
34177                         this.onDenyColumnHide();
34178                         return false;
34179                     }
34180                     cm.setHidden(index, item.checked);
34181                 }
34182         }
34183         return true;
34184     },
34185
34186     beforeColMenuShow : function(){
34187         var cm = this.cm,  colCount = cm.getColumnCount();
34188         this.colMenu.removeAll();
34189         for(var i = 0; i < colCount; i++){
34190             this.colMenu.add(new Roo.menu.CheckItem({
34191                 id: "col-"+cm.getColumnId(i),
34192                 text: cm.getColumnHeader(i),
34193                 checked: !cm.isHidden(i),
34194                 hideOnClick:false
34195             }));
34196         }
34197     },
34198
34199     handleHdCtx : function(g, index, e){
34200         e.stopEvent();
34201         var hd = this.getHeaderCell(index);
34202         this.hdCtxIndex = index;
34203         var ms = this.hmenu.items, cm = this.cm;
34204         ms.get("asc").setDisabled(!cm.isSortable(index));
34205         ms.get("desc").setDisabled(!cm.isSortable(index));
34206         if(this.grid.enableColLock !== false){
34207             ms.get("lock").setDisabled(cm.isLocked(index));
34208             ms.get("unlock").setDisabled(!cm.isLocked(index));
34209         }
34210         this.hmenu.show(hd, "tl-bl");
34211     },
34212
34213     handleHdOver : function(e){
34214         var hd = this.findHeaderCell(e.getTarget());
34215         if(hd && !this.headersDisabled){
34216             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
34217                this.fly(hd).addClass("x-grid-hd-over");
34218             }
34219         }
34220     },
34221
34222     handleHdOut : function(e){
34223         var hd = this.findHeaderCell(e.getTarget());
34224         if(hd){
34225             this.fly(hd).removeClass("x-grid-hd-over");
34226         }
34227     },
34228
34229     handleSplitDblClick : function(e, t){
34230         var i = this.getCellIndex(t);
34231         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
34232             this.autoSizeColumn(i, true);
34233             this.layout();
34234         }
34235     },
34236
34237     render : function(){
34238
34239         var cm = this.cm;
34240         var colCount = cm.getColumnCount();
34241
34242         if(this.grid.monitorWindowResize === true){
34243             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34244         }
34245         var header = this.renderHeaders();
34246         var body = this.templates.body.apply({rows:""});
34247         var html = this.templates.master.apply({
34248             lockedBody: body,
34249             body: body,
34250             lockedHeader: header[0],
34251             header: header[1]
34252         });
34253
34254         //this.updateColumns();
34255
34256         this.grid.getGridEl().dom.innerHTML = html;
34257
34258         this.initElements();
34259         
34260         // a kludge to fix the random scolling effect in webkit
34261         this.el.on("scroll", function() {
34262             this.el.dom.scrollTop=0; // hopefully not recursive..
34263         },this);
34264
34265         this.scroller.on("scroll", this.handleScroll, this);
34266         this.lockedBody.on("mousewheel", this.handleWheel, this);
34267         this.mainBody.on("mousewheel", this.handleWheel, this);
34268
34269         this.mainHd.on("mouseover", this.handleHdOver, this);
34270         this.mainHd.on("mouseout", this.handleHdOut, this);
34271         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
34272                 {delegate: "."+this.splitClass});
34273
34274         this.lockedHd.on("mouseover", this.handleHdOver, this);
34275         this.lockedHd.on("mouseout", this.handleHdOut, this);
34276         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
34277                 {delegate: "."+this.splitClass});
34278
34279         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
34280             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34281         }
34282
34283         this.updateSplitters();
34284
34285         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
34286             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34287             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34288         }
34289
34290         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
34291             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
34292             this.hmenu.add(
34293                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
34294                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
34295             );
34296             if(this.grid.enableColLock !== false){
34297                 this.hmenu.add('-',
34298                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
34299                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
34300                 );
34301             }
34302             if (Roo.isTouch) {
34303                  this.hmenu.add('-',
34304                     {id:"wider", text: this.columnsWiderText},
34305                     {id:"narrow", text: this.columnsNarrowText }
34306                 );
34307                 
34308                  
34309             }
34310             
34311             if(this.grid.enableColumnHide !== false){
34312
34313                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
34314                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
34315                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
34316
34317                 this.hmenu.add('-',
34318                     {id:"columns", text: this.columnsText, menu: this.colMenu}
34319                 );
34320             }
34321             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
34322
34323             this.grid.on("headercontextmenu", this.handleHdCtx, this);
34324         }
34325
34326         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
34327             this.dd = new Roo.grid.GridDragZone(this.grid, {
34328                 ddGroup : this.grid.ddGroup || 'GridDD'
34329             });
34330             
34331         }
34332
34333         /*
34334         for(var i = 0; i < colCount; i++){
34335             if(cm.isHidden(i)){
34336                 this.hideColumn(i);
34337             }
34338             if(cm.config[i].align){
34339                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
34340                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
34341             }
34342         }*/
34343         
34344         this.updateHeaderSortState();
34345
34346         this.beforeInitialResize();
34347         this.layout(true);
34348
34349         // two part rendering gives faster view to the user
34350         this.renderPhase2.defer(1, this);
34351     },
34352
34353     renderPhase2 : function(){
34354         // render the rows now
34355         this.refresh();
34356         if(this.grid.autoSizeColumns){
34357             this.autoSizeColumns();
34358         }
34359     },
34360
34361     beforeInitialResize : function(){
34362
34363     },
34364
34365     onColumnSplitterMoved : function(i, w){
34366         this.userResized = true;
34367         var cm = this.grid.colModel;
34368         cm.setColumnWidth(i, w, true);
34369         var cid = cm.getColumnId(i);
34370         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34371         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34372         this.updateSplitters();
34373         this.layout();
34374         this.grid.fireEvent("columnresize", i, w);
34375     },
34376
34377     syncRowHeights : function(startIndex, endIndex){
34378         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
34379             startIndex = startIndex || 0;
34380             var mrows = this.getBodyTable().rows;
34381             var lrows = this.getLockedTable().rows;
34382             var len = mrows.length-1;
34383             endIndex = Math.min(endIndex || len, len);
34384             for(var i = startIndex; i <= endIndex; i++){
34385                 var m = mrows[i], l = lrows[i];
34386                 var h = Math.max(m.offsetHeight, l.offsetHeight);
34387                 m.style.height = l.style.height = h + "px";
34388             }
34389         }
34390     },
34391
34392     layout : function(initialRender, is2ndPass){
34393         var g = this.grid;
34394         var auto = g.autoHeight;
34395         var scrollOffset = 16;
34396         var c = g.getGridEl(), cm = this.cm,
34397                 expandCol = g.autoExpandColumn,
34398                 gv = this;
34399         //c.beginMeasure();
34400
34401         if(!c.dom.offsetWidth){ // display:none?
34402             if(initialRender){
34403                 this.lockedWrap.show();
34404                 this.mainWrap.show();
34405             }
34406             return;
34407         }
34408
34409         var hasLock = this.cm.isLocked(0);
34410
34411         var tbh = this.headerPanel.getHeight();
34412         var bbh = this.footerPanel.getHeight();
34413
34414         if(auto){
34415             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
34416             var newHeight = ch + c.getBorderWidth("tb");
34417             if(g.maxHeight){
34418                 newHeight = Math.min(g.maxHeight, newHeight);
34419             }
34420             c.setHeight(newHeight);
34421         }
34422
34423         if(g.autoWidth){
34424             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
34425         }
34426
34427         var s = this.scroller;
34428
34429         var csize = c.getSize(true);
34430
34431         this.el.setSize(csize.width, csize.height);
34432
34433         this.headerPanel.setWidth(csize.width);
34434         this.footerPanel.setWidth(csize.width);
34435
34436         var hdHeight = this.mainHd.getHeight();
34437         var vw = csize.width;
34438         var vh = csize.height - (tbh + bbh);
34439
34440         s.setSize(vw, vh);
34441
34442         var bt = this.getBodyTable();
34443         
34444         if(cm.getLockedCount() == cm.config.length){
34445             bt = this.getLockedTable();
34446         }
34447         
34448         var ltWidth = hasLock ?
34449                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
34450
34451         var scrollHeight = bt.offsetHeight;
34452         var scrollWidth = ltWidth + bt.offsetWidth;
34453         var vscroll = false, hscroll = false;
34454
34455         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
34456
34457         var lw = this.lockedWrap, mw = this.mainWrap;
34458         var lb = this.lockedBody, mb = this.mainBody;
34459
34460         setTimeout(function(){
34461             var t = s.dom.offsetTop;
34462             var w = s.dom.clientWidth,
34463                 h = s.dom.clientHeight;
34464
34465             lw.setTop(t);
34466             lw.setSize(ltWidth, h);
34467
34468             mw.setLeftTop(ltWidth, t);
34469             mw.setSize(w-ltWidth, h);
34470
34471             lb.setHeight(h-hdHeight);
34472             mb.setHeight(h-hdHeight);
34473
34474             if(is2ndPass !== true && !gv.userResized && expandCol){
34475                 // high speed resize without full column calculation
34476                 
34477                 var ci = cm.getIndexById(expandCol);
34478                 if (ci < 0) {
34479                     ci = cm.findColumnIndex(expandCol);
34480                 }
34481                 ci = Math.max(0, ci); // make sure it's got at least the first col.
34482                 var expandId = cm.getColumnId(ci);
34483                 var  tw = cm.getTotalWidth(false);
34484                 var currentWidth = cm.getColumnWidth(ci);
34485                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
34486                 if(currentWidth != cw){
34487                     cm.setColumnWidth(ci, cw, true);
34488                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34489                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34490                     gv.updateSplitters();
34491                     gv.layout(false, true);
34492                 }
34493             }
34494
34495             if(initialRender){
34496                 lw.show();
34497                 mw.show();
34498             }
34499             //c.endMeasure();
34500         }, 10);
34501     },
34502
34503     onWindowResize : function(){
34504         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
34505             return;
34506         }
34507         this.layout();
34508     },
34509
34510     appendFooter : function(parentEl){
34511         return null;
34512     },
34513
34514     sortAscText : "Sort Ascending",
34515     sortDescText : "Sort Descending",
34516     lockText : "Lock Column",
34517     unlockText : "Unlock Column",
34518     columnsText : "Columns",
34519  
34520     columnsWiderText : "Wider",
34521     columnsNarrowText : "Thinner"
34522 });
34523
34524
34525 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
34526     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
34527     this.proxy.el.addClass('x-grid3-col-dd');
34528 };
34529
34530 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
34531     handleMouseDown : function(e){
34532
34533     },
34534
34535     callHandleMouseDown : function(e){
34536         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
34537     }
34538 });
34539 /*
34540  * Based on:
34541  * Ext JS Library 1.1.1
34542  * Copyright(c) 2006-2007, Ext JS, LLC.
34543  *
34544  * Originally Released Under LGPL - original licence link has changed is not relivant.
34545  *
34546  * Fork - LGPL
34547  * <script type="text/javascript">
34548  */
34549  
34550 // private
34551 // This is a support class used internally by the Grid components
34552 Roo.grid.SplitDragZone = function(grid, hd, hd2){
34553     this.grid = grid;
34554     this.view = grid.getView();
34555     this.proxy = this.view.resizeProxy;
34556     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
34557         "gridSplitters" + this.grid.getGridEl().id, {
34558         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
34559     });
34560     this.setHandleElId(Roo.id(hd));
34561     this.setOuterHandleElId(Roo.id(hd2));
34562     this.scroll = false;
34563 };
34564 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
34565     fly: Roo.Element.fly,
34566
34567     b4StartDrag : function(x, y){
34568         this.view.headersDisabled = true;
34569         this.proxy.setHeight(this.view.mainWrap.getHeight());
34570         var w = this.cm.getColumnWidth(this.cellIndex);
34571         var minw = Math.max(w-this.grid.minColumnWidth, 0);
34572         this.resetConstraints();
34573         this.setXConstraint(minw, 1000);
34574         this.setYConstraint(0, 0);
34575         this.minX = x - minw;
34576         this.maxX = x + 1000;
34577         this.startPos = x;
34578         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
34579     },
34580
34581
34582     handleMouseDown : function(e){
34583         ev = Roo.EventObject.setEvent(e);
34584         var t = this.fly(ev.getTarget());
34585         if(t.hasClass("x-grid-split")){
34586             this.cellIndex = this.view.getCellIndex(t.dom);
34587             this.split = t.dom;
34588             this.cm = this.grid.colModel;
34589             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
34590                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
34591             }
34592         }
34593     },
34594
34595     endDrag : function(e){
34596         this.view.headersDisabled = false;
34597         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
34598         var diff = endX - this.startPos;
34599         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
34600     },
34601
34602     autoOffset : function(){
34603         this.setDelta(0,0);
34604     }
34605 });/*
34606  * Based on:
34607  * Ext JS Library 1.1.1
34608  * Copyright(c) 2006-2007, Ext JS, LLC.
34609  *
34610  * Originally Released Under LGPL - original licence link has changed is not relivant.
34611  *
34612  * Fork - LGPL
34613  * <script type="text/javascript">
34614  */
34615  
34616 // private
34617 // This is a support class used internally by the Grid components
34618 Roo.grid.GridDragZone = function(grid, config){
34619     this.view = grid.getView();
34620     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
34621     if(this.view.lockedBody){
34622         this.setHandleElId(Roo.id(this.view.mainBody.dom));
34623         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
34624     }
34625     this.scroll = false;
34626     this.grid = grid;
34627     this.ddel = document.createElement('div');
34628     this.ddel.className = 'x-grid-dd-wrap';
34629 };
34630
34631 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
34632     ddGroup : "GridDD",
34633
34634     getDragData : function(e){
34635         var t = Roo.lib.Event.getTarget(e);
34636         var rowIndex = this.view.findRowIndex(t);
34637         var sm = this.grid.selModel;
34638             
34639         //Roo.log(rowIndex);
34640         
34641         if (sm.getSelectedCell) {
34642             // cell selection..
34643             if (!sm.getSelectedCell()) {
34644                 return false;
34645             }
34646             if (rowIndex != sm.getSelectedCell()[0]) {
34647                 return false;
34648             }
34649         
34650         }
34651         
34652         if(rowIndex !== false){
34653             
34654             // if editorgrid.. 
34655             
34656             
34657             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
34658                
34659             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
34660               //  
34661             //}
34662             if (e.hasModifier()){
34663                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
34664             }
34665             
34666             Roo.log("getDragData");
34667             
34668             return {
34669                 grid: this.grid,
34670                 ddel: this.ddel,
34671                 rowIndex: rowIndex,
34672                 selections:sm.getSelections ? sm.getSelections() : (
34673                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
34674                 )
34675             };
34676         }
34677         return false;
34678     },
34679
34680     onInitDrag : function(e){
34681         var data = this.dragData;
34682         this.ddel.innerHTML = this.grid.getDragDropText();
34683         this.proxy.update(this.ddel);
34684         // fire start drag?
34685     },
34686
34687     afterRepair : function(){
34688         this.dragging = false;
34689     },
34690
34691     getRepairXY : function(e, data){
34692         return false;
34693     },
34694
34695     onEndDrag : function(data, e){
34696         // fire end drag?
34697     },
34698
34699     onValidDrop : function(dd, e, id){
34700         // fire drag drop?
34701         this.hideProxy();
34702     },
34703
34704     beforeInvalidDrop : function(e, id){
34705
34706     }
34707 });/*
34708  * Based on:
34709  * Ext JS Library 1.1.1
34710  * Copyright(c) 2006-2007, Ext JS, LLC.
34711  *
34712  * Originally Released Under LGPL - original licence link has changed is not relivant.
34713  *
34714  * Fork - LGPL
34715  * <script type="text/javascript">
34716  */
34717  
34718
34719 /**
34720  * @class Roo.grid.ColumnModel
34721  * @extends Roo.util.Observable
34722  * This is the default implementation of a ColumnModel used by the Grid. It defines
34723  * the columns in the grid.
34724  * <br>Usage:<br>
34725  <pre><code>
34726  var colModel = new Roo.grid.ColumnModel([
34727         {header: "Ticker", width: 60, sortable: true, locked: true},
34728         {header: "Company Name", width: 150, sortable: true},
34729         {header: "Market Cap.", width: 100, sortable: true},
34730         {header: "$ Sales", width: 100, sortable: true, renderer: money},
34731         {header: "Employees", width: 100, sortable: true, resizable: false}
34732  ]);
34733  </code></pre>
34734  * <p>
34735  
34736  * The config options listed for this class are options which may appear in each
34737  * individual column definition.
34738  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
34739  * @constructor
34740  * @param {Object} config An Array of column config objects. See this class's
34741  * config objects for details.
34742 */
34743 Roo.grid.ColumnModel = function(config){
34744         /**
34745      * The config passed into the constructor
34746      */
34747     this.config = config;
34748     this.lookup = {};
34749
34750     // if no id, create one
34751     // if the column does not have a dataIndex mapping,
34752     // map it to the order it is in the config
34753     for(var i = 0, len = config.length; i < len; i++){
34754         var c = config[i];
34755         if(typeof c.dataIndex == "undefined"){
34756             c.dataIndex = i;
34757         }
34758         if(typeof c.renderer == "string"){
34759             c.renderer = Roo.util.Format[c.renderer];
34760         }
34761         if(typeof c.id == "undefined"){
34762             c.id = Roo.id();
34763         }
34764         if(c.editor && c.editor.xtype){
34765             c.editor  = Roo.factory(c.editor, Roo.grid);
34766         }
34767         if(c.editor && c.editor.isFormField){
34768             c.editor = new Roo.grid.GridEditor(c.editor);
34769         }
34770         this.lookup[c.id] = c;
34771     }
34772
34773     /**
34774      * The width of columns which have no width specified (defaults to 100)
34775      * @type Number
34776      */
34777     this.defaultWidth = 100;
34778
34779     /**
34780      * Default sortable of columns which have no sortable specified (defaults to false)
34781      * @type Boolean
34782      */
34783     this.defaultSortable = false;
34784
34785     this.addEvents({
34786         /**
34787              * @event widthchange
34788              * Fires when the width of a column changes.
34789              * @param {ColumnModel} this
34790              * @param {Number} columnIndex The column index
34791              * @param {Number} newWidth The new width
34792              */
34793             "widthchange": true,
34794         /**
34795              * @event headerchange
34796              * Fires when the text of a header changes.
34797              * @param {ColumnModel} this
34798              * @param {Number} columnIndex The column index
34799              * @param {Number} newText The new header text
34800              */
34801             "headerchange": true,
34802         /**
34803              * @event hiddenchange
34804              * Fires when a column is hidden or "unhidden".
34805              * @param {ColumnModel} this
34806              * @param {Number} columnIndex The column index
34807              * @param {Boolean} hidden true if hidden, false otherwise
34808              */
34809             "hiddenchange": true,
34810             /**
34811          * @event columnmoved
34812          * Fires when a column is moved.
34813          * @param {ColumnModel} this
34814          * @param {Number} oldIndex
34815          * @param {Number} newIndex
34816          */
34817         "columnmoved" : true,
34818         /**
34819          * @event columlockchange
34820          * Fires when a column's locked state is changed
34821          * @param {ColumnModel} this
34822          * @param {Number} colIndex
34823          * @param {Boolean} locked true if locked
34824          */
34825         "columnlockchange" : true
34826     });
34827     Roo.grid.ColumnModel.superclass.constructor.call(this);
34828 };
34829 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
34830     /**
34831      * @cfg {String} header The header text to display in the Grid view.
34832      */
34833     /**
34834      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
34835      * {@link Roo.data.Record} definition from which to draw the column's value. If not
34836      * specified, the column's index is used as an index into the Record's data Array.
34837      */
34838     /**
34839      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
34840      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
34841      */
34842     /**
34843      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
34844      * Defaults to the value of the {@link #defaultSortable} property.
34845      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
34846      */
34847     /**
34848      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
34849      */
34850     /**
34851      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
34852      */
34853     /**
34854      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
34855      */
34856     /**
34857      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
34858      */
34859     /**
34860      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
34861      * given the cell's data value. See {@link #setRenderer}. If not specified, the
34862      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
34863      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
34864      */
34865        /**
34866      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
34867      */
34868     /**
34869      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
34870      */
34871     /**
34872      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
34873      */
34874     /**
34875      * @cfg {String} cursor (Optional)
34876      */
34877     /**
34878      * @cfg {String} tooltip (Optional)
34879      */
34880     /**
34881      * @cfg {Number} xs (Optional)
34882      */
34883     /**
34884      * @cfg {Number} sm (Optional)
34885      */
34886     /**
34887      * @cfg {Number} md (Optional)
34888      */
34889     /**
34890      * @cfg {Number} lg (Optional)
34891      */
34892     /**
34893      * Returns the id of the column at the specified index.
34894      * @param {Number} index The column index
34895      * @return {String} the id
34896      */
34897     getColumnId : function(index){
34898         return this.config[index].id;
34899     },
34900
34901     /**
34902      * Returns the column for a specified id.
34903      * @param {String} id The column id
34904      * @return {Object} the column
34905      */
34906     getColumnById : function(id){
34907         return this.lookup[id];
34908     },
34909
34910     
34911     /**
34912      * Returns the column for a specified dataIndex.
34913      * @param {String} dataIndex The column dataIndex
34914      * @return {Object|Boolean} the column or false if not found
34915      */
34916     getColumnByDataIndex: function(dataIndex){
34917         var index = this.findColumnIndex(dataIndex);
34918         return index > -1 ? this.config[index] : false;
34919     },
34920     
34921     /**
34922      * Returns the index for a specified column id.
34923      * @param {String} id The column id
34924      * @return {Number} the index, or -1 if not found
34925      */
34926     getIndexById : function(id){
34927         for(var i = 0, len = this.config.length; i < len; i++){
34928             if(this.config[i].id == id){
34929                 return i;
34930             }
34931         }
34932         return -1;
34933     },
34934     
34935     /**
34936      * Returns the index for a specified column dataIndex.
34937      * @param {String} dataIndex The column dataIndex
34938      * @return {Number} the index, or -1 if not found
34939      */
34940     
34941     findColumnIndex : function(dataIndex){
34942         for(var i = 0, len = this.config.length; i < len; i++){
34943             if(this.config[i].dataIndex == dataIndex){
34944                 return i;
34945             }
34946         }
34947         return -1;
34948     },
34949     
34950     
34951     moveColumn : function(oldIndex, newIndex){
34952         var c = this.config[oldIndex];
34953         this.config.splice(oldIndex, 1);
34954         this.config.splice(newIndex, 0, c);
34955         this.dataMap = null;
34956         this.fireEvent("columnmoved", this, oldIndex, newIndex);
34957     },
34958
34959     isLocked : function(colIndex){
34960         return this.config[colIndex].locked === true;
34961     },
34962
34963     setLocked : function(colIndex, value, suppressEvent){
34964         if(this.isLocked(colIndex) == value){
34965             return;
34966         }
34967         this.config[colIndex].locked = value;
34968         if(!suppressEvent){
34969             this.fireEvent("columnlockchange", this, colIndex, value);
34970         }
34971     },
34972
34973     getTotalLockedWidth : function(){
34974         var totalWidth = 0;
34975         for(var i = 0; i < this.config.length; i++){
34976             if(this.isLocked(i) && !this.isHidden(i)){
34977                 this.totalWidth += this.getColumnWidth(i);
34978             }
34979         }
34980         return totalWidth;
34981     },
34982
34983     getLockedCount : function(){
34984         for(var i = 0, len = this.config.length; i < len; i++){
34985             if(!this.isLocked(i)){
34986                 return i;
34987             }
34988         }
34989         
34990         return this.config.length;
34991     },
34992
34993     /**
34994      * Returns the number of columns.
34995      * @return {Number}
34996      */
34997     getColumnCount : function(visibleOnly){
34998         if(visibleOnly === true){
34999             var c = 0;
35000             for(var i = 0, len = this.config.length; i < len; i++){
35001                 if(!this.isHidden(i)){
35002                     c++;
35003                 }
35004             }
35005             return c;
35006         }
35007         return this.config.length;
35008     },
35009
35010     /**
35011      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
35012      * @param {Function} fn
35013      * @param {Object} scope (optional)
35014      * @return {Array} result
35015      */
35016     getColumnsBy : function(fn, scope){
35017         var r = [];
35018         for(var i = 0, len = this.config.length; i < len; i++){
35019             var c = this.config[i];
35020             if(fn.call(scope||this, c, i) === true){
35021                 r[r.length] = c;
35022             }
35023         }
35024         return r;
35025     },
35026
35027     /**
35028      * Returns true if the specified column is sortable.
35029      * @param {Number} col The column index
35030      * @return {Boolean}
35031      */
35032     isSortable : function(col){
35033         if(typeof this.config[col].sortable == "undefined"){
35034             return this.defaultSortable;
35035         }
35036         return this.config[col].sortable;
35037     },
35038
35039     /**
35040      * Returns the rendering (formatting) function defined for the column.
35041      * @param {Number} col The column index.
35042      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
35043      */
35044     getRenderer : function(col){
35045         if(!this.config[col].renderer){
35046             return Roo.grid.ColumnModel.defaultRenderer;
35047         }
35048         return this.config[col].renderer;
35049     },
35050
35051     /**
35052      * Sets the rendering (formatting) function for a column.
35053      * @param {Number} col The column index
35054      * @param {Function} fn The function to use to process the cell's raw data
35055      * to return HTML markup for the grid view. The render function is called with
35056      * the following parameters:<ul>
35057      * <li>Data value.</li>
35058      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
35059      * <li>css A CSS style string to apply to the table cell.</li>
35060      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
35061      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
35062      * <li>Row index</li>
35063      * <li>Column index</li>
35064      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
35065      */
35066     setRenderer : function(col, fn){
35067         this.config[col].renderer = fn;
35068     },
35069
35070     /**
35071      * Returns the width for the specified column.
35072      * @param {Number} col The column index
35073      * @return {Number}
35074      */
35075     getColumnWidth : function(col){
35076         return this.config[col].width * 1 || this.defaultWidth;
35077     },
35078
35079     /**
35080      * Sets the width for a column.
35081      * @param {Number} col The column index
35082      * @param {Number} width The new width
35083      */
35084     setColumnWidth : function(col, width, suppressEvent){
35085         this.config[col].width = width;
35086         this.totalWidth = null;
35087         if(!suppressEvent){
35088              this.fireEvent("widthchange", this, col, width);
35089         }
35090     },
35091
35092     /**
35093      * Returns the total width of all columns.
35094      * @param {Boolean} includeHidden True to include hidden column widths
35095      * @return {Number}
35096      */
35097     getTotalWidth : function(includeHidden){
35098         if(!this.totalWidth){
35099             this.totalWidth = 0;
35100             for(var i = 0, len = this.config.length; i < len; i++){
35101                 if(includeHidden || !this.isHidden(i)){
35102                     this.totalWidth += this.getColumnWidth(i);
35103                 }
35104             }
35105         }
35106         return this.totalWidth;
35107     },
35108
35109     /**
35110      * Returns the header for the specified column.
35111      * @param {Number} col The column index
35112      * @return {String}
35113      */
35114     getColumnHeader : function(col){
35115         return this.config[col].header;
35116     },
35117
35118     /**
35119      * Sets the header for a column.
35120      * @param {Number} col The column index
35121      * @param {String} header The new header
35122      */
35123     setColumnHeader : function(col, header){
35124         this.config[col].header = header;
35125         this.fireEvent("headerchange", this, col, header);
35126     },
35127
35128     /**
35129      * Returns the tooltip for the specified column.
35130      * @param {Number} col The column index
35131      * @return {String}
35132      */
35133     getColumnTooltip : function(col){
35134             return this.config[col].tooltip;
35135     },
35136     /**
35137      * Sets the tooltip for a column.
35138      * @param {Number} col The column index
35139      * @param {String} tooltip The new tooltip
35140      */
35141     setColumnTooltip : function(col, tooltip){
35142             this.config[col].tooltip = tooltip;
35143     },
35144
35145     /**
35146      * Returns the dataIndex for the specified column.
35147      * @param {Number} col The column index
35148      * @return {Number}
35149      */
35150     getDataIndex : function(col){
35151         return this.config[col].dataIndex;
35152     },
35153
35154     /**
35155      * Sets the dataIndex for a column.
35156      * @param {Number} col The column index
35157      * @param {Number} dataIndex The new dataIndex
35158      */
35159     setDataIndex : function(col, dataIndex){
35160         this.config[col].dataIndex = dataIndex;
35161     },
35162
35163     
35164     
35165     /**
35166      * Returns true if the cell is editable.
35167      * @param {Number} colIndex The column index
35168      * @param {Number} rowIndex The row index - this is nto actually used..?
35169      * @return {Boolean}
35170      */
35171     isCellEditable : function(colIndex, rowIndex){
35172         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
35173     },
35174
35175     /**
35176      * Returns the editor defined for the cell/column.
35177      * return false or null to disable editing.
35178      * @param {Number} colIndex The column index
35179      * @param {Number} rowIndex The row index
35180      * @return {Object}
35181      */
35182     getCellEditor : function(colIndex, rowIndex){
35183         return this.config[colIndex].editor;
35184     },
35185
35186     /**
35187      * Sets if a column is editable.
35188      * @param {Number} col The column index
35189      * @param {Boolean} editable True if the column is editable
35190      */
35191     setEditable : function(col, editable){
35192         this.config[col].editable = editable;
35193     },
35194
35195
35196     /**
35197      * Returns true if the column is hidden.
35198      * @param {Number} colIndex The column index
35199      * @return {Boolean}
35200      */
35201     isHidden : function(colIndex){
35202         return this.config[colIndex].hidden;
35203     },
35204
35205
35206     /**
35207      * Returns true if the column width cannot be changed
35208      */
35209     isFixed : function(colIndex){
35210         return this.config[colIndex].fixed;
35211     },
35212
35213     /**
35214      * Returns true if the column can be resized
35215      * @return {Boolean}
35216      */
35217     isResizable : function(colIndex){
35218         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
35219     },
35220     /**
35221      * Sets if a column is hidden.
35222      * @param {Number} colIndex The column index
35223      * @param {Boolean} hidden True if the column is hidden
35224      */
35225     setHidden : function(colIndex, hidden){
35226         this.config[colIndex].hidden = hidden;
35227         this.totalWidth = null;
35228         this.fireEvent("hiddenchange", this, colIndex, hidden);
35229     },
35230
35231     /**
35232      * Sets the editor for a column.
35233      * @param {Number} col The column index
35234      * @param {Object} editor The editor object
35235      */
35236     setEditor : function(col, editor){
35237         this.config[col].editor = editor;
35238     }
35239 });
35240
35241 Roo.grid.ColumnModel.defaultRenderer = function(value)
35242 {
35243     if(typeof value == "object") {
35244         return value;
35245     }
35246         if(typeof value == "string" && value.length < 1){
35247             return "&#160;";
35248         }
35249     
35250         return String.format("{0}", value);
35251 };
35252
35253 // Alias for backwards compatibility
35254 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
35255 /*
35256  * Based on:
35257  * Ext JS Library 1.1.1
35258  * Copyright(c) 2006-2007, Ext JS, LLC.
35259  *
35260  * Originally Released Under LGPL - original licence link has changed is not relivant.
35261  *
35262  * Fork - LGPL
35263  * <script type="text/javascript">
35264  */
35265
35266 /**
35267  * @class Roo.grid.AbstractSelectionModel
35268  * @extends Roo.util.Observable
35269  * Abstract base class for grid SelectionModels.  It provides the interface that should be
35270  * implemented by descendant classes.  This class should not be directly instantiated.
35271  * @constructor
35272  */
35273 Roo.grid.AbstractSelectionModel = function(){
35274     this.locked = false;
35275     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
35276 };
35277
35278 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
35279     /** @ignore Called by the grid automatically. Do not call directly. */
35280     init : function(grid){
35281         this.grid = grid;
35282         this.initEvents();
35283     },
35284
35285     /**
35286      * Locks the selections.
35287      */
35288     lock : function(){
35289         this.locked = true;
35290     },
35291
35292     /**
35293      * Unlocks the selections.
35294      */
35295     unlock : function(){
35296         this.locked = false;
35297     },
35298
35299     /**
35300      * Returns true if the selections are locked.
35301      * @return {Boolean}
35302      */
35303     isLocked : function(){
35304         return this.locked;
35305     }
35306 });/*
35307  * Based on:
35308  * Ext JS Library 1.1.1
35309  * Copyright(c) 2006-2007, Ext JS, LLC.
35310  *
35311  * Originally Released Under LGPL - original licence link has changed is not relivant.
35312  *
35313  * Fork - LGPL
35314  * <script type="text/javascript">
35315  */
35316 /**
35317  * @extends Roo.grid.AbstractSelectionModel
35318  * @class Roo.grid.RowSelectionModel
35319  * The default SelectionModel used by {@link Roo.grid.Grid}.
35320  * It supports multiple selections and keyboard selection/navigation. 
35321  * @constructor
35322  * @param {Object} config
35323  */
35324 Roo.grid.RowSelectionModel = function(config){
35325     Roo.apply(this, config);
35326     this.selections = new Roo.util.MixedCollection(false, function(o){
35327         return o.id;
35328     });
35329
35330     this.last = false;
35331     this.lastActive = false;
35332
35333     this.addEvents({
35334         /**
35335              * @event selectionchange
35336              * Fires when the selection changes
35337              * @param {SelectionModel} this
35338              */
35339             "selectionchange" : true,
35340         /**
35341              * @event afterselectionchange
35342              * Fires after the selection changes (eg. by key press or clicking)
35343              * @param {SelectionModel} this
35344              */
35345             "afterselectionchange" : true,
35346         /**
35347              * @event beforerowselect
35348              * Fires when a row is selected being selected, return false to cancel.
35349              * @param {SelectionModel} this
35350              * @param {Number} rowIndex The selected index
35351              * @param {Boolean} keepExisting False if other selections will be cleared
35352              */
35353             "beforerowselect" : true,
35354         /**
35355              * @event rowselect
35356              * Fires when a row is selected.
35357              * @param {SelectionModel} this
35358              * @param {Number} rowIndex The selected index
35359              * @param {Roo.data.Record} r The record
35360              */
35361             "rowselect" : true,
35362         /**
35363              * @event rowdeselect
35364              * Fires when a row is deselected.
35365              * @param {SelectionModel} this
35366              * @param {Number} rowIndex The selected index
35367              */
35368         "rowdeselect" : true
35369     });
35370     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
35371     this.locked = false;
35372 };
35373
35374 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
35375     /**
35376      * @cfg {Boolean} singleSelect
35377      * True to allow selection of only one row at a time (defaults to false)
35378      */
35379     singleSelect : false,
35380
35381     // private
35382     initEvents : function(){
35383
35384         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
35385             this.grid.on("mousedown", this.handleMouseDown, this);
35386         }else{ // allow click to work like normal
35387             this.grid.on("rowclick", this.handleDragableRowClick, this);
35388         }
35389
35390         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
35391             "up" : function(e){
35392                 if(!e.shiftKey){
35393                     this.selectPrevious(e.shiftKey);
35394                 }else if(this.last !== false && this.lastActive !== false){
35395                     var last = this.last;
35396                     this.selectRange(this.last,  this.lastActive-1);
35397                     this.grid.getView().focusRow(this.lastActive);
35398                     if(last !== false){
35399                         this.last = last;
35400                     }
35401                 }else{
35402                     this.selectFirstRow();
35403                 }
35404                 this.fireEvent("afterselectionchange", this);
35405             },
35406             "down" : function(e){
35407                 if(!e.shiftKey){
35408                     this.selectNext(e.shiftKey);
35409                 }else if(this.last !== false && this.lastActive !== false){
35410                     var last = this.last;
35411                     this.selectRange(this.last,  this.lastActive+1);
35412                     this.grid.getView().focusRow(this.lastActive);
35413                     if(last !== false){
35414                         this.last = last;
35415                     }
35416                 }else{
35417                     this.selectFirstRow();
35418                 }
35419                 this.fireEvent("afterselectionchange", this);
35420             },
35421             scope: this
35422         });
35423
35424         var view = this.grid.view;
35425         view.on("refresh", this.onRefresh, this);
35426         view.on("rowupdated", this.onRowUpdated, this);
35427         view.on("rowremoved", this.onRemove, this);
35428     },
35429
35430     // private
35431     onRefresh : function(){
35432         var ds = this.grid.dataSource, i, v = this.grid.view;
35433         var s = this.selections;
35434         s.each(function(r){
35435             if((i = ds.indexOfId(r.id)) != -1){
35436                 v.onRowSelect(i);
35437                 s.add(ds.getAt(i)); // updating the selection relate data
35438             }else{
35439                 s.remove(r);
35440             }
35441         });
35442     },
35443
35444     // private
35445     onRemove : function(v, index, r){
35446         this.selections.remove(r);
35447     },
35448
35449     // private
35450     onRowUpdated : function(v, index, r){
35451         if(this.isSelected(r)){
35452             v.onRowSelect(index);
35453         }
35454     },
35455
35456     /**
35457      * Select records.
35458      * @param {Array} records The records to select
35459      * @param {Boolean} keepExisting (optional) True to keep existing selections
35460      */
35461     selectRecords : function(records, keepExisting){
35462         if(!keepExisting){
35463             this.clearSelections();
35464         }
35465         var ds = this.grid.dataSource;
35466         for(var i = 0, len = records.length; i < len; i++){
35467             this.selectRow(ds.indexOf(records[i]), true);
35468         }
35469     },
35470
35471     /**
35472      * Gets the number of selected rows.
35473      * @return {Number}
35474      */
35475     getCount : function(){
35476         return this.selections.length;
35477     },
35478
35479     /**
35480      * Selects the first row in the grid.
35481      */
35482     selectFirstRow : function(){
35483         this.selectRow(0);
35484     },
35485
35486     /**
35487      * Select the last row.
35488      * @param {Boolean} keepExisting (optional) True to keep existing selections
35489      */
35490     selectLastRow : function(keepExisting){
35491         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
35492     },
35493
35494     /**
35495      * Selects the row immediately following the last selected row.
35496      * @param {Boolean} keepExisting (optional) True to keep existing selections
35497      */
35498     selectNext : function(keepExisting){
35499         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
35500             this.selectRow(this.last+1, keepExisting);
35501             this.grid.getView().focusRow(this.last);
35502         }
35503     },
35504
35505     /**
35506      * Selects the row that precedes the last selected row.
35507      * @param {Boolean} keepExisting (optional) True to keep existing selections
35508      */
35509     selectPrevious : function(keepExisting){
35510         if(this.last){
35511             this.selectRow(this.last-1, keepExisting);
35512             this.grid.getView().focusRow(this.last);
35513         }
35514     },
35515
35516     /**
35517      * Returns the selected records
35518      * @return {Array} Array of selected records
35519      */
35520     getSelections : function(){
35521         return [].concat(this.selections.items);
35522     },
35523
35524     /**
35525      * Returns the first selected record.
35526      * @return {Record}
35527      */
35528     getSelected : function(){
35529         return this.selections.itemAt(0);
35530     },
35531
35532
35533     /**
35534      * Clears all selections.
35535      */
35536     clearSelections : function(fast){
35537         if(this.locked) {
35538             return;
35539         }
35540         if(fast !== true){
35541             var ds = this.grid.dataSource;
35542             var s = this.selections;
35543             s.each(function(r){
35544                 this.deselectRow(ds.indexOfId(r.id));
35545             }, this);
35546             s.clear();
35547         }else{
35548             this.selections.clear();
35549         }
35550         this.last = false;
35551     },
35552
35553
35554     /**
35555      * Selects all rows.
35556      */
35557     selectAll : function(){
35558         if(this.locked) {
35559             return;
35560         }
35561         this.selections.clear();
35562         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
35563             this.selectRow(i, true);
35564         }
35565     },
35566
35567     /**
35568      * Returns True if there is a selection.
35569      * @return {Boolean}
35570      */
35571     hasSelection : function(){
35572         return this.selections.length > 0;
35573     },
35574
35575     /**
35576      * Returns True if the specified row is selected.
35577      * @param {Number/Record} record The record or index of the record to check
35578      * @return {Boolean}
35579      */
35580     isSelected : function(index){
35581         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
35582         return (r && this.selections.key(r.id) ? true : false);
35583     },
35584
35585     /**
35586      * Returns True if the specified record id is selected.
35587      * @param {String} id The id of record to check
35588      * @return {Boolean}
35589      */
35590     isIdSelected : function(id){
35591         return (this.selections.key(id) ? true : false);
35592     },
35593
35594     // private
35595     handleMouseDown : function(e, t){
35596         var view = this.grid.getView(), rowIndex;
35597         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
35598             return;
35599         };
35600         if(e.shiftKey && this.last !== false){
35601             var last = this.last;
35602             this.selectRange(last, rowIndex, e.ctrlKey);
35603             this.last = last; // reset the last
35604             view.focusRow(rowIndex);
35605         }else{
35606             var isSelected = this.isSelected(rowIndex);
35607             if(e.button !== 0 && isSelected){
35608                 view.focusRow(rowIndex);
35609             }else if(e.ctrlKey && isSelected){
35610                 this.deselectRow(rowIndex);
35611             }else if(!isSelected){
35612                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
35613                 view.focusRow(rowIndex);
35614             }
35615         }
35616         this.fireEvent("afterselectionchange", this);
35617     },
35618     // private
35619     handleDragableRowClick :  function(grid, rowIndex, e) 
35620     {
35621         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
35622             this.selectRow(rowIndex, false);
35623             grid.view.focusRow(rowIndex);
35624              this.fireEvent("afterselectionchange", this);
35625         }
35626     },
35627     
35628     /**
35629      * Selects multiple rows.
35630      * @param {Array} rows Array of the indexes of the row to select
35631      * @param {Boolean} keepExisting (optional) True to keep existing selections
35632      */
35633     selectRows : function(rows, keepExisting){
35634         if(!keepExisting){
35635             this.clearSelections();
35636         }
35637         for(var i = 0, len = rows.length; i < len; i++){
35638             this.selectRow(rows[i], true);
35639         }
35640     },
35641
35642     /**
35643      * Selects a range of rows. All rows in between startRow and endRow are also selected.
35644      * @param {Number} startRow The index of the first row in the range
35645      * @param {Number} endRow The index of the last row in the range
35646      * @param {Boolean} keepExisting (optional) True to retain existing selections
35647      */
35648     selectRange : function(startRow, endRow, keepExisting){
35649         if(this.locked) {
35650             return;
35651         }
35652         if(!keepExisting){
35653             this.clearSelections();
35654         }
35655         if(startRow <= endRow){
35656             for(var i = startRow; i <= endRow; i++){
35657                 this.selectRow(i, true);
35658             }
35659         }else{
35660             for(var i = startRow; i >= endRow; i--){
35661                 this.selectRow(i, true);
35662             }
35663         }
35664     },
35665
35666     /**
35667      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
35668      * @param {Number} startRow The index of the first row in the range
35669      * @param {Number} endRow The index of the last row in the range
35670      */
35671     deselectRange : function(startRow, endRow, preventViewNotify){
35672         if(this.locked) {
35673             return;
35674         }
35675         for(var i = startRow; i <= endRow; i++){
35676             this.deselectRow(i, preventViewNotify);
35677         }
35678     },
35679
35680     /**
35681      * Selects a row.
35682      * @param {Number} row The index of the row to select
35683      * @param {Boolean} keepExisting (optional) True to keep existing selections
35684      */
35685     selectRow : function(index, keepExisting, preventViewNotify){
35686         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
35687             return;
35688         }
35689         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
35690             if(!keepExisting || this.singleSelect){
35691                 this.clearSelections();
35692             }
35693             var r = this.grid.dataSource.getAt(index);
35694             this.selections.add(r);
35695             this.last = this.lastActive = index;
35696             if(!preventViewNotify){
35697                 this.grid.getView().onRowSelect(index);
35698             }
35699             this.fireEvent("rowselect", this, index, r);
35700             this.fireEvent("selectionchange", this);
35701         }
35702     },
35703
35704     /**
35705      * Deselects a row.
35706      * @param {Number} row The index of the row to deselect
35707      */
35708     deselectRow : function(index, preventViewNotify){
35709         if(this.locked) {
35710             return;
35711         }
35712         if(this.last == index){
35713             this.last = false;
35714         }
35715         if(this.lastActive == index){
35716             this.lastActive = false;
35717         }
35718         var r = this.grid.dataSource.getAt(index);
35719         this.selections.remove(r);
35720         if(!preventViewNotify){
35721             this.grid.getView().onRowDeselect(index);
35722         }
35723         this.fireEvent("rowdeselect", this, index);
35724         this.fireEvent("selectionchange", this);
35725     },
35726
35727     // private
35728     restoreLast : function(){
35729         if(this._last){
35730             this.last = this._last;
35731         }
35732     },
35733
35734     // private
35735     acceptsNav : function(row, col, cm){
35736         return !cm.isHidden(col) && cm.isCellEditable(col, row);
35737     },
35738
35739     // private
35740     onEditorKey : function(field, e){
35741         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
35742         if(k == e.TAB){
35743             e.stopEvent();
35744             ed.completeEdit();
35745             if(e.shiftKey){
35746                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
35747             }else{
35748                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
35749             }
35750         }else if(k == e.ENTER && !e.ctrlKey){
35751             e.stopEvent();
35752             ed.completeEdit();
35753             if(e.shiftKey){
35754                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
35755             }else{
35756                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
35757             }
35758         }else if(k == e.ESC){
35759             ed.cancelEdit();
35760         }
35761         if(newCell){
35762             g.startEditing(newCell[0], newCell[1]);
35763         }
35764     }
35765 });/*
35766  * Based on:
35767  * Ext JS Library 1.1.1
35768  * Copyright(c) 2006-2007, Ext JS, LLC.
35769  *
35770  * Originally Released Under LGPL - original licence link has changed is not relivant.
35771  *
35772  * Fork - LGPL
35773  * <script type="text/javascript">
35774  */
35775 /**
35776  * @class Roo.grid.CellSelectionModel
35777  * @extends Roo.grid.AbstractSelectionModel
35778  * This class provides the basic implementation for cell selection in a grid.
35779  * @constructor
35780  * @param {Object} config The object containing the configuration of this model.
35781  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
35782  */
35783 Roo.grid.CellSelectionModel = function(config){
35784     Roo.apply(this, config);
35785
35786     this.selection = null;
35787
35788     this.addEvents({
35789         /**
35790              * @event beforerowselect
35791              * Fires before a cell is selected.
35792              * @param {SelectionModel} this
35793              * @param {Number} rowIndex The selected row index
35794              * @param {Number} colIndex The selected cell index
35795              */
35796             "beforecellselect" : true,
35797         /**
35798              * @event cellselect
35799              * Fires when a cell is selected.
35800              * @param {SelectionModel} this
35801              * @param {Number} rowIndex The selected row index
35802              * @param {Number} colIndex The selected cell index
35803              */
35804             "cellselect" : true,
35805         /**
35806              * @event selectionchange
35807              * Fires when the active selection changes.
35808              * @param {SelectionModel} this
35809              * @param {Object} selection null for no selection or an object (o) with two properties
35810                 <ul>
35811                 <li>o.record: the record object for the row the selection is in</li>
35812                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
35813                 </ul>
35814              */
35815             "selectionchange" : true,
35816         /**
35817              * @event tabend
35818              * Fires when the tab (or enter) was pressed on the last editable cell
35819              * You can use this to trigger add new row.
35820              * @param {SelectionModel} this
35821              */
35822             "tabend" : true,
35823          /**
35824              * @event beforeeditnext
35825              * Fires before the next editable sell is made active
35826              * You can use this to skip to another cell or fire the tabend
35827              *    if you set cell to false
35828              * @param {Object} eventdata object : { cell : [ row, col ] } 
35829              */
35830             "beforeeditnext" : true
35831     });
35832     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
35833 };
35834
35835 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
35836     
35837     enter_is_tab: false,
35838
35839     /** @ignore */
35840     initEvents : function(){
35841         this.grid.on("mousedown", this.handleMouseDown, this);
35842         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
35843         var view = this.grid.view;
35844         view.on("refresh", this.onViewChange, this);
35845         view.on("rowupdated", this.onRowUpdated, this);
35846         view.on("beforerowremoved", this.clearSelections, this);
35847         view.on("beforerowsinserted", this.clearSelections, this);
35848         if(this.grid.isEditor){
35849             this.grid.on("beforeedit", this.beforeEdit,  this);
35850         }
35851     },
35852
35853         //private
35854     beforeEdit : function(e){
35855         this.select(e.row, e.column, false, true, e.record);
35856     },
35857
35858         //private
35859     onRowUpdated : function(v, index, r){
35860         if(this.selection && this.selection.record == r){
35861             v.onCellSelect(index, this.selection.cell[1]);
35862         }
35863     },
35864
35865         //private
35866     onViewChange : function(){
35867         this.clearSelections(true);
35868     },
35869
35870         /**
35871          * Returns the currently selected cell,.
35872          * @return {Array} The selected cell (row, column) or null if none selected.
35873          */
35874     getSelectedCell : function(){
35875         return this.selection ? this.selection.cell : null;
35876     },
35877
35878     /**
35879      * Clears all selections.
35880      * @param {Boolean} true to prevent the gridview from being notified about the change.
35881      */
35882     clearSelections : function(preventNotify){
35883         var s = this.selection;
35884         if(s){
35885             if(preventNotify !== true){
35886                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
35887             }
35888             this.selection = null;
35889             this.fireEvent("selectionchange", this, null);
35890         }
35891     },
35892
35893     /**
35894      * Returns true if there is a selection.
35895      * @return {Boolean}
35896      */
35897     hasSelection : function(){
35898         return this.selection ? true : false;
35899     },
35900
35901     /** @ignore */
35902     handleMouseDown : function(e, t){
35903         var v = this.grid.getView();
35904         if(this.isLocked()){
35905             return;
35906         };
35907         var row = v.findRowIndex(t);
35908         var cell = v.findCellIndex(t);
35909         if(row !== false && cell !== false){
35910             this.select(row, cell);
35911         }
35912     },
35913
35914     /**
35915      * Selects a cell.
35916      * @param {Number} rowIndex
35917      * @param {Number} collIndex
35918      */
35919     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
35920         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
35921             this.clearSelections();
35922             r = r || this.grid.dataSource.getAt(rowIndex);
35923             this.selection = {
35924                 record : r,
35925                 cell : [rowIndex, colIndex]
35926             };
35927             if(!preventViewNotify){
35928                 var v = this.grid.getView();
35929                 v.onCellSelect(rowIndex, colIndex);
35930                 if(preventFocus !== true){
35931                     v.focusCell(rowIndex, colIndex);
35932                 }
35933             }
35934             this.fireEvent("cellselect", this, rowIndex, colIndex);
35935             this.fireEvent("selectionchange", this, this.selection);
35936         }
35937     },
35938
35939         //private
35940     isSelectable : function(rowIndex, colIndex, cm){
35941         return !cm.isHidden(colIndex);
35942     },
35943
35944     /** @ignore */
35945     handleKeyDown : function(e){
35946         //Roo.log('Cell Sel Model handleKeyDown');
35947         if(!e.isNavKeyPress()){
35948             return;
35949         }
35950         var g = this.grid, s = this.selection;
35951         if(!s){
35952             e.stopEvent();
35953             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
35954             if(cell){
35955                 this.select(cell[0], cell[1]);
35956             }
35957             return;
35958         }
35959         var sm = this;
35960         var walk = function(row, col, step){
35961             return g.walkCells(row, col, step, sm.isSelectable,  sm);
35962         };
35963         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
35964         var newCell;
35965
35966       
35967
35968         switch(k){
35969             case e.TAB:
35970                 // handled by onEditorKey
35971                 if (g.isEditor && g.editing) {
35972                     return;
35973                 }
35974                 if(e.shiftKey) {
35975                     newCell = walk(r, c-1, -1);
35976                 } else {
35977                     newCell = walk(r, c+1, 1);
35978                 }
35979                 break;
35980             
35981             case e.DOWN:
35982                newCell = walk(r+1, c, 1);
35983                 break;
35984             
35985             case e.UP:
35986                 newCell = walk(r-1, c, -1);
35987                 break;
35988             
35989             case e.RIGHT:
35990                 newCell = walk(r, c+1, 1);
35991                 break;
35992             
35993             case e.LEFT:
35994                 newCell = walk(r, c-1, -1);
35995                 break;
35996             
35997             case e.ENTER:
35998                 
35999                 if(g.isEditor && !g.editing){
36000                    g.startEditing(r, c);
36001                    e.stopEvent();
36002                    return;
36003                 }
36004                 
36005                 
36006              break;
36007         };
36008         if(newCell){
36009             this.select(newCell[0], newCell[1]);
36010             e.stopEvent();
36011             
36012         }
36013     },
36014
36015     acceptsNav : function(row, col, cm){
36016         return !cm.isHidden(col) && cm.isCellEditable(col, row);
36017     },
36018     /**
36019      * Selects a cell.
36020      * @param {Number} field (not used) - as it's normally used as a listener
36021      * @param {Number} e - event - fake it by using
36022      *
36023      * var e = Roo.EventObjectImpl.prototype;
36024      * e.keyCode = e.TAB
36025      *
36026      * 
36027      */
36028     onEditorKey : function(field, e){
36029         
36030         var k = e.getKey(),
36031             newCell,
36032             g = this.grid,
36033             ed = g.activeEditor,
36034             forward = false;
36035         ///Roo.log('onEditorKey' + k);
36036         
36037         
36038         if (this.enter_is_tab && k == e.ENTER) {
36039             k = e.TAB;
36040         }
36041         
36042         if(k == e.TAB){
36043             if(e.shiftKey){
36044                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36045             }else{
36046                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36047                 forward = true;
36048             }
36049             
36050             e.stopEvent();
36051             
36052         } else if(k == e.ENTER &&  !e.ctrlKey){
36053             ed.completeEdit();
36054             e.stopEvent();
36055             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36056         
36057                 } else if(k == e.ESC){
36058             ed.cancelEdit();
36059         }
36060                 
36061         if (newCell) {
36062             var ecall = { cell : newCell, forward : forward };
36063             this.fireEvent('beforeeditnext', ecall );
36064             newCell = ecall.cell;
36065                         forward = ecall.forward;
36066         }
36067                 
36068         if(newCell){
36069             //Roo.log('next cell after edit');
36070             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
36071         } else if (forward) {
36072             // tabbed past last
36073             this.fireEvent.defer(100, this, ['tabend',this]);
36074         }
36075     }
36076 });/*
36077  * Based on:
36078  * Ext JS Library 1.1.1
36079  * Copyright(c) 2006-2007, Ext JS, LLC.
36080  *
36081  * Originally Released Under LGPL - original licence link has changed is not relivant.
36082  *
36083  * Fork - LGPL
36084  * <script type="text/javascript">
36085  */
36086  
36087 /**
36088  * @class Roo.grid.EditorGrid
36089  * @extends Roo.grid.Grid
36090  * Class for creating and editable grid.
36091  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
36092  * The container MUST have some type of size defined for the grid to fill. The container will be 
36093  * automatically set to position relative if it isn't already.
36094  * @param {Object} dataSource The data model to bind to
36095  * @param {Object} colModel The column model with info about this grid's columns
36096  */
36097 Roo.grid.EditorGrid = function(container, config){
36098     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
36099     this.getGridEl().addClass("xedit-grid");
36100
36101     if(!this.selModel){
36102         this.selModel = new Roo.grid.CellSelectionModel();
36103     }
36104
36105     this.activeEditor = null;
36106
36107         this.addEvents({
36108             /**
36109              * @event beforeedit
36110              * Fires before cell editing is triggered. The edit event object has the following properties <br />
36111              * <ul style="padding:5px;padding-left:16px;">
36112              * <li>grid - This grid</li>
36113              * <li>record - The record being edited</li>
36114              * <li>field - The field name being edited</li>
36115              * <li>value - The value for the field being edited.</li>
36116              * <li>row - The grid row index</li>
36117              * <li>column - The grid column index</li>
36118              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36119              * </ul>
36120              * @param {Object} e An edit event (see above for description)
36121              */
36122             "beforeedit" : true,
36123             /**
36124              * @event afteredit
36125              * Fires after a cell is edited. <br />
36126              * <ul style="padding:5px;padding-left:16px;">
36127              * <li>grid - This grid</li>
36128              * <li>record - The record being edited</li>
36129              * <li>field - The field name being edited</li>
36130              * <li>value - The value being set</li>
36131              * <li>originalValue - The original value for the field, before the edit.</li>
36132              * <li>row - The grid row index</li>
36133              * <li>column - The grid column index</li>
36134              * </ul>
36135              * @param {Object} e An edit event (see above for description)
36136              */
36137             "afteredit" : true,
36138             /**
36139              * @event validateedit
36140              * Fires after a cell is edited, but before the value is set in the record. 
36141          * You can use this to modify the value being set in the field, Return false
36142              * to cancel the change. The edit event object has the following properties <br />
36143              * <ul style="padding:5px;padding-left:16px;">
36144          * <li>editor - This editor</li>
36145              * <li>grid - This grid</li>
36146              * <li>record - The record being edited</li>
36147              * <li>field - The field name being edited</li>
36148              * <li>value - The value being set</li>
36149              * <li>originalValue - The original value for the field, before the edit.</li>
36150              * <li>row - The grid row index</li>
36151              * <li>column - The grid column index</li>
36152              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36153              * </ul>
36154              * @param {Object} e An edit event (see above for description)
36155              */
36156             "validateedit" : true
36157         });
36158     this.on("bodyscroll", this.stopEditing,  this);
36159     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
36160 };
36161
36162 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
36163     /**
36164      * @cfg {Number} clicksToEdit
36165      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
36166      */
36167     clicksToEdit: 2,
36168
36169     // private
36170     isEditor : true,
36171     // private
36172     trackMouseOver: false, // causes very odd FF errors
36173
36174     onCellDblClick : function(g, row, col){
36175         this.startEditing(row, col);
36176     },
36177
36178     onEditComplete : function(ed, value, startValue){
36179         this.editing = false;
36180         this.activeEditor = null;
36181         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
36182         var r = ed.record;
36183         var field = this.colModel.getDataIndex(ed.col);
36184         var e = {
36185             grid: this,
36186             record: r,
36187             field: field,
36188             originalValue: startValue,
36189             value: value,
36190             row: ed.row,
36191             column: ed.col,
36192             cancel:false,
36193             editor: ed
36194         };
36195         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
36196         cell.show();
36197           
36198         if(String(value) !== String(startValue)){
36199             
36200             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
36201                 r.set(field, e.value);
36202                 // if we are dealing with a combo box..
36203                 // then we also set the 'name' colum to be the displayField
36204                 if (ed.field.displayField && ed.field.name) {
36205                     r.set(ed.field.name, ed.field.el.dom.value);
36206                 }
36207                 
36208                 delete e.cancel; //?? why!!!
36209                 this.fireEvent("afteredit", e);
36210             }
36211         } else {
36212             this.fireEvent("afteredit", e); // always fire it!
36213         }
36214         this.view.focusCell(ed.row, ed.col);
36215     },
36216
36217     /**
36218      * Starts editing the specified for the specified row/column
36219      * @param {Number} rowIndex
36220      * @param {Number} colIndex
36221      */
36222     startEditing : function(row, col){
36223         this.stopEditing();
36224         if(this.colModel.isCellEditable(col, row)){
36225             this.view.ensureVisible(row, col, true);
36226           
36227             var r = this.dataSource.getAt(row);
36228             var field = this.colModel.getDataIndex(col);
36229             var cell = Roo.get(this.view.getCell(row,col));
36230             var e = {
36231                 grid: this,
36232                 record: r,
36233                 field: field,
36234                 value: r.data[field],
36235                 row: row,
36236                 column: col,
36237                 cancel:false 
36238             };
36239             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
36240                 this.editing = true;
36241                 var ed = this.colModel.getCellEditor(col, row);
36242                 
36243                 if (!ed) {
36244                     return;
36245                 }
36246                 if(!ed.rendered){
36247                     ed.render(ed.parentEl || document.body);
36248                 }
36249                 ed.field.reset();
36250                
36251                 cell.hide();
36252                 
36253                 (function(){ // complex but required for focus issues in safari, ie and opera
36254                     ed.row = row;
36255                     ed.col = col;
36256                     ed.record = r;
36257                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
36258                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
36259                     this.activeEditor = ed;
36260                     var v = r.data[field];
36261                     ed.startEdit(this.view.getCell(row, col), v);
36262                     // combo's with 'displayField and name set
36263                     if (ed.field.displayField && ed.field.name) {
36264                         ed.field.el.dom.value = r.data[ed.field.name];
36265                     }
36266                     
36267                     
36268                 }).defer(50, this);
36269             }
36270         }
36271     },
36272         
36273     /**
36274      * Stops any active editing
36275      */
36276     stopEditing : function(){
36277         if(this.activeEditor){
36278             this.activeEditor.completeEdit();
36279         }
36280         this.activeEditor = null;
36281     },
36282         
36283          /**
36284      * Called to get grid's drag proxy text, by default returns this.ddText.
36285      * @return {String}
36286      */
36287     getDragDropText : function(){
36288         var count = this.selModel.getSelectedCell() ? 1 : 0;
36289         return String.format(this.ddText, count, count == 1 ? '' : 's');
36290     }
36291         
36292 });/*
36293  * Based on:
36294  * Ext JS Library 1.1.1
36295  * Copyright(c) 2006-2007, Ext JS, LLC.
36296  *
36297  * Originally Released Under LGPL - original licence link has changed is not relivant.
36298  *
36299  * Fork - LGPL
36300  * <script type="text/javascript">
36301  */
36302
36303 // private - not really -- you end up using it !
36304 // This is a support class used internally by the Grid components
36305
36306 /**
36307  * @class Roo.grid.GridEditor
36308  * @extends Roo.Editor
36309  * Class for creating and editable grid elements.
36310  * @param {Object} config any settings (must include field)
36311  */
36312 Roo.grid.GridEditor = function(field, config){
36313     if (!config && field.field) {
36314         config = field;
36315         field = Roo.factory(config.field, Roo.form);
36316     }
36317     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
36318     field.monitorTab = false;
36319 };
36320
36321 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
36322     
36323     /**
36324      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
36325      */
36326     
36327     alignment: "tl-tl",
36328     autoSize: "width",
36329     hideEl : false,
36330     cls: "x-small-editor x-grid-editor",
36331     shim:false,
36332     shadow:"frame"
36333 });/*
36334  * Based on:
36335  * Ext JS Library 1.1.1
36336  * Copyright(c) 2006-2007, Ext JS, LLC.
36337  *
36338  * Originally Released Under LGPL - original licence link has changed is not relivant.
36339  *
36340  * Fork - LGPL
36341  * <script type="text/javascript">
36342  */
36343   
36344
36345   
36346 Roo.grid.PropertyRecord = Roo.data.Record.create([
36347     {name:'name',type:'string'},  'value'
36348 ]);
36349
36350
36351 Roo.grid.PropertyStore = function(grid, source){
36352     this.grid = grid;
36353     this.store = new Roo.data.Store({
36354         recordType : Roo.grid.PropertyRecord
36355     });
36356     this.store.on('update', this.onUpdate,  this);
36357     if(source){
36358         this.setSource(source);
36359     }
36360     Roo.grid.PropertyStore.superclass.constructor.call(this);
36361 };
36362
36363
36364
36365 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
36366     setSource : function(o){
36367         this.source = o;
36368         this.store.removeAll();
36369         var data = [];
36370         for(var k in o){
36371             if(this.isEditableValue(o[k])){
36372                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
36373             }
36374         }
36375         this.store.loadRecords({records: data}, {}, true);
36376     },
36377
36378     onUpdate : function(ds, record, type){
36379         if(type == Roo.data.Record.EDIT){
36380             var v = record.data['value'];
36381             var oldValue = record.modified['value'];
36382             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
36383                 this.source[record.id] = v;
36384                 record.commit();
36385                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
36386             }else{
36387                 record.reject();
36388             }
36389         }
36390     },
36391
36392     getProperty : function(row){
36393        return this.store.getAt(row);
36394     },
36395
36396     isEditableValue: function(val){
36397         if(val && val instanceof Date){
36398             return true;
36399         }else if(typeof val == 'object' || typeof val == 'function'){
36400             return false;
36401         }
36402         return true;
36403     },
36404
36405     setValue : function(prop, value){
36406         this.source[prop] = value;
36407         this.store.getById(prop).set('value', value);
36408     },
36409
36410     getSource : function(){
36411         return this.source;
36412     }
36413 });
36414
36415 Roo.grid.PropertyColumnModel = function(grid, store){
36416     this.grid = grid;
36417     var g = Roo.grid;
36418     g.PropertyColumnModel.superclass.constructor.call(this, [
36419         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
36420         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
36421     ]);
36422     this.store = store;
36423     this.bselect = Roo.DomHelper.append(document.body, {
36424         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
36425             {tag: 'option', value: 'true', html: 'true'},
36426             {tag: 'option', value: 'false', html: 'false'}
36427         ]
36428     });
36429     Roo.id(this.bselect);
36430     var f = Roo.form;
36431     this.editors = {
36432         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
36433         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
36434         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
36435         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
36436         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
36437     };
36438     this.renderCellDelegate = this.renderCell.createDelegate(this);
36439     this.renderPropDelegate = this.renderProp.createDelegate(this);
36440 };
36441
36442 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
36443     
36444     
36445     nameText : 'Name',
36446     valueText : 'Value',
36447     
36448     dateFormat : 'm/j/Y',
36449     
36450     
36451     renderDate : function(dateVal){
36452         return dateVal.dateFormat(this.dateFormat);
36453     },
36454
36455     renderBool : function(bVal){
36456         return bVal ? 'true' : 'false';
36457     },
36458
36459     isCellEditable : function(colIndex, rowIndex){
36460         return colIndex == 1;
36461     },
36462
36463     getRenderer : function(col){
36464         return col == 1 ?
36465             this.renderCellDelegate : this.renderPropDelegate;
36466     },
36467
36468     renderProp : function(v){
36469         return this.getPropertyName(v);
36470     },
36471
36472     renderCell : function(val){
36473         var rv = val;
36474         if(val instanceof Date){
36475             rv = this.renderDate(val);
36476         }else if(typeof val == 'boolean'){
36477             rv = this.renderBool(val);
36478         }
36479         return Roo.util.Format.htmlEncode(rv);
36480     },
36481
36482     getPropertyName : function(name){
36483         var pn = this.grid.propertyNames;
36484         return pn && pn[name] ? pn[name] : name;
36485     },
36486
36487     getCellEditor : function(colIndex, rowIndex){
36488         var p = this.store.getProperty(rowIndex);
36489         var n = p.data['name'], val = p.data['value'];
36490         
36491         if(typeof(this.grid.customEditors[n]) == 'string'){
36492             return this.editors[this.grid.customEditors[n]];
36493         }
36494         if(typeof(this.grid.customEditors[n]) != 'undefined'){
36495             return this.grid.customEditors[n];
36496         }
36497         if(val instanceof Date){
36498             return this.editors['date'];
36499         }else if(typeof val == 'number'){
36500             return this.editors['number'];
36501         }else if(typeof val == 'boolean'){
36502             return this.editors['boolean'];
36503         }else{
36504             return this.editors['string'];
36505         }
36506     }
36507 });
36508
36509 /**
36510  * @class Roo.grid.PropertyGrid
36511  * @extends Roo.grid.EditorGrid
36512  * This class represents the  interface of a component based property grid control.
36513  * <br><br>Usage:<pre><code>
36514  var grid = new Roo.grid.PropertyGrid("my-container-id", {
36515       
36516  });
36517  // set any options
36518  grid.render();
36519  * </code></pre>
36520   
36521  * @constructor
36522  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
36523  * The container MUST have some type of size defined for the grid to fill. The container will be
36524  * automatically set to position relative if it isn't already.
36525  * @param {Object} config A config object that sets properties on this grid.
36526  */
36527 Roo.grid.PropertyGrid = function(container, config){
36528     config = config || {};
36529     var store = new Roo.grid.PropertyStore(this);
36530     this.store = store;
36531     var cm = new Roo.grid.PropertyColumnModel(this, store);
36532     store.store.sort('name', 'ASC');
36533     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
36534         ds: store.store,
36535         cm: cm,
36536         enableColLock:false,
36537         enableColumnMove:false,
36538         stripeRows:false,
36539         trackMouseOver: false,
36540         clicksToEdit:1
36541     }, config));
36542     this.getGridEl().addClass('x-props-grid');
36543     this.lastEditRow = null;
36544     this.on('columnresize', this.onColumnResize, this);
36545     this.addEvents({
36546          /**
36547              * @event beforepropertychange
36548              * Fires before a property changes (return false to stop?)
36549              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
36550              * @param {String} id Record Id
36551              * @param {String} newval New Value
36552          * @param {String} oldval Old Value
36553              */
36554         "beforepropertychange": true,
36555         /**
36556              * @event propertychange
36557              * Fires after a property changes
36558              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
36559              * @param {String} id Record Id
36560              * @param {String} newval New Value
36561          * @param {String} oldval Old Value
36562              */
36563         "propertychange": true
36564     });
36565     this.customEditors = this.customEditors || {};
36566 };
36567 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
36568     
36569      /**
36570      * @cfg {Object} customEditors map of colnames=> custom editors.
36571      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
36572      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
36573      * false disables editing of the field.
36574          */
36575     
36576       /**
36577      * @cfg {Object} propertyNames map of property Names to their displayed value
36578          */
36579     
36580     render : function(){
36581         Roo.grid.PropertyGrid.superclass.render.call(this);
36582         this.autoSize.defer(100, this);
36583     },
36584
36585     autoSize : function(){
36586         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
36587         if(this.view){
36588             this.view.fitColumns();
36589         }
36590     },
36591
36592     onColumnResize : function(){
36593         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
36594         this.autoSize();
36595     },
36596     /**
36597      * Sets the data for the Grid
36598      * accepts a Key => Value object of all the elements avaiable.
36599      * @param {Object} data  to appear in grid.
36600      */
36601     setSource : function(source){
36602         this.store.setSource(source);
36603         //this.autoSize();
36604     },
36605     /**
36606      * Gets all the data from the grid.
36607      * @return {Object} data  data stored in grid
36608      */
36609     getSource : function(){
36610         return this.store.getSource();
36611     }
36612 });/*
36613   
36614  * Licence LGPL
36615  
36616  */
36617  
36618 /**
36619  * @class Roo.grid.Calendar
36620  * @extends Roo.util.Grid
36621  * This class extends the Grid to provide a calendar widget
36622  * <br><br>Usage:<pre><code>
36623  var grid = new Roo.grid.Calendar("my-container-id", {
36624      ds: myDataStore,
36625      cm: myColModel,
36626      selModel: mySelectionModel,
36627      autoSizeColumns: true,
36628      monitorWindowResize: false,
36629      trackMouseOver: true
36630      eventstore : real data store..
36631  });
36632  // set any options
36633  grid.render();
36634   
36635   * @constructor
36636  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
36637  * The container MUST have some type of size defined for the grid to fill. The container will be
36638  * automatically set to position relative if it isn't already.
36639  * @param {Object} config A config object that sets properties on this grid.
36640  */
36641 Roo.grid.Calendar = function(container, config){
36642         // initialize the container
36643         this.container = Roo.get(container);
36644         this.container.update("");
36645         this.container.setStyle("overflow", "hidden");
36646     this.container.addClass('x-grid-container');
36647
36648     this.id = this.container.id;
36649
36650     Roo.apply(this, config);
36651     // check and correct shorthanded configs
36652     
36653     var rows = [];
36654     var d =1;
36655     for (var r = 0;r < 6;r++) {
36656         
36657         rows[r]=[];
36658         for (var c =0;c < 7;c++) {
36659             rows[r][c]= '';
36660         }
36661     }
36662     if (this.eventStore) {
36663         this.eventStore= Roo.factory(this.eventStore, Roo.data);
36664         this.eventStore.on('load',this.onLoad, this);
36665         this.eventStore.on('beforeload',this.clearEvents, this);
36666          
36667     }
36668     
36669     this.dataSource = new Roo.data.Store({
36670             proxy: new Roo.data.MemoryProxy(rows),
36671             reader: new Roo.data.ArrayReader({}, [
36672                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
36673     });
36674
36675     this.dataSource.load();
36676     this.ds = this.dataSource;
36677     this.ds.xmodule = this.xmodule || false;
36678     
36679     
36680     var cellRender = function(v,x,r)
36681     {
36682         return String.format(
36683             '<div class="fc-day  fc-widget-content"><div>' +
36684                 '<div class="fc-event-container"></div>' +
36685                 '<div class="fc-day-number">{0}</div>'+
36686                 
36687                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
36688             '</div></div>', v);
36689     
36690     }
36691     
36692     
36693     this.colModel = new Roo.grid.ColumnModel( [
36694         {
36695             xtype: 'ColumnModel',
36696             xns: Roo.grid,
36697             dataIndex : 'weekday0',
36698             header : 'Sunday',
36699             renderer : cellRender
36700         },
36701         {
36702             xtype: 'ColumnModel',
36703             xns: Roo.grid,
36704             dataIndex : 'weekday1',
36705             header : 'Monday',
36706             renderer : cellRender
36707         },
36708         {
36709             xtype: 'ColumnModel',
36710             xns: Roo.grid,
36711             dataIndex : 'weekday2',
36712             header : 'Tuesday',
36713             renderer : cellRender
36714         },
36715         {
36716             xtype: 'ColumnModel',
36717             xns: Roo.grid,
36718             dataIndex : 'weekday3',
36719             header : 'Wednesday',
36720             renderer : cellRender
36721         },
36722         {
36723             xtype: 'ColumnModel',
36724             xns: Roo.grid,
36725             dataIndex : 'weekday4',
36726             header : 'Thursday',
36727             renderer : cellRender
36728         },
36729         {
36730             xtype: 'ColumnModel',
36731             xns: Roo.grid,
36732             dataIndex : 'weekday5',
36733             header : 'Friday',
36734             renderer : cellRender
36735         },
36736         {
36737             xtype: 'ColumnModel',
36738             xns: Roo.grid,
36739             dataIndex : 'weekday6',
36740             header : 'Saturday',
36741             renderer : cellRender
36742         }
36743     ]);
36744     this.cm = this.colModel;
36745     this.cm.xmodule = this.xmodule || false;
36746  
36747         
36748           
36749     //this.selModel = new Roo.grid.CellSelectionModel();
36750     //this.sm = this.selModel;
36751     //this.selModel.init(this);
36752     
36753     
36754     if(this.width){
36755         this.container.setWidth(this.width);
36756     }
36757
36758     if(this.height){
36759         this.container.setHeight(this.height);
36760     }
36761     /** @private */
36762         this.addEvents({
36763         // raw events
36764         /**
36765          * @event click
36766          * The raw click event for the entire grid.
36767          * @param {Roo.EventObject} e
36768          */
36769         "click" : true,
36770         /**
36771          * @event dblclick
36772          * The raw dblclick event for the entire grid.
36773          * @param {Roo.EventObject} e
36774          */
36775         "dblclick" : true,
36776         /**
36777          * @event contextmenu
36778          * The raw contextmenu event for the entire grid.
36779          * @param {Roo.EventObject} e
36780          */
36781         "contextmenu" : true,
36782         /**
36783          * @event mousedown
36784          * The raw mousedown event for the entire grid.
36785          * @param {Roo.EventObject} e
36786          */
36787         "mousedown" : true,
36788         /**
36789          * @event mouseup
36790          * The raw mouseup event for the entire grid.
36791          * @param {Roo.EventObject} e
36792          */
36793         "mouseup" : true,
36794         /**
36795          * @event mouseover
36796          * The raw mouseover event for the entire grid.
36797          * @param {Roo.EventObject} e
36798          */
36799         "mouseover" : true,
36800         /**
36801          * @event mouseout
36802          * The raw mouseout event for the entire grid.
36803          * @param {Roo.EventObject} e
36804          */
36805         "mouseout" : true,
36806         /**
36807          * @event keypress
36808          * The raw keypress event for the entire grid.
36809          * @param {Roo.EventObject} e
36810          */
36811         "keypress" : true,
36812         /**
36813          * @event keydown
36814          * The raw keydown event for the entire grid.
36815          * @param {Roo.EventObject} e
36816          */
36817         "keydown" : true,
36818
36819         // custom events
36820
36821         /**
36822          * @event cellclick
36823          * Fires when a cell is clicked
36824          * @param {Grid} this
36825          * @param {Number} rowIndex
36826          * @param {Number} columnIndex
36827          * @param {Roo.EventObject} e
36828          */
36829         "cellclick" : true,
36830         /**
36831          * @event celldblclick
36832          * Fires when a cell is double clicked
36833          * @param {Grid} this
36834          * @param {Number} rowIndex
36835          * @param {Number} columnIndex
36836          * @param {Roo.EventObject} e
36837          */
36838         "celldblclick" : true,
36839         /**
36840          * @event rowclick
36841          * Fires when a row is clicked
36842          * @param {Grid} this
36843          * @param {Number} rowIndex
36844          * @param {Roo.EventObject} e
36845          */
36846         "rowclick" : true,
36847         /**
36848          * @event rowdblclick
36849          * Fires when a row is double clicked
36850          * @param {Grid} this
36851          * @param {Number} rowIndex
36852          * @param {Roo.EventObject} e
36853          */
36854         "rowdblclick" : true,
36855         /**
36856          * @event headerclick
36857          * Fires when a header is clicked
36858          * @param {Grid} this
36859          * @param {Number} columnIndex
36860          * @param {Roo.EventObject} e
36861          */
36862         "headerclick" : true,
36863         /**
36864          * @event headerdblclick
36865          * Fires when a header cell is double clicked
36866          * @param {Grid} this
36867          * @param {Number} columnIndex
36868          * @param {Roo.EventObject} e
36869          */
36870         "headerdblclick" : true,
36871         /**
36872          * @event rowcontextmenu
36873          * Fires when a row is right clicked
36874          * @param {Grid} this
36875          * @param {Number} rowIndex
36876          * @param {Roo.EventObject} e
36877          */
36878         "rowcontextmenu" : true,
36879         /**
36880          * @event cellcontextmenu
36881          * Fires when a cell is right clicked
36882          * @param {Grid} this
36883          * @param {Number} rowIndex
36884          * @param {Number} cellIndex
36885          * @param {Roo.EventObject} e
36886          */
36887          "cellcontextmenu" : true,
36888         /**
36889          * @event headercontextmenu
36890          * Fires when a header is right clicked
36891          * @param {Grid} this
36892          * @param {Number} columnIndex
36893          * @param {Roo.EventObject} e
36894          */
36895         "headercontextmenu" : true,
36896         /**
36897          * @event bodyscroll
36898          * Fires when the body element is scrolled
36899          * @param {Number} scrollLeft
36900          * @param {Number} scrollTop
36901          */
36902         "bodyscroll" : true,
36903         /**
36904          * @event columnresize
36905          * Fires when the user resizes a column
36906          * @param {Number} columnIndex
36907          * @param {Number} newSize
36908          */
36909         "columnresize" : true,
36910         /**
36911          * @event columnmove
36912          * Fires when the user moves a column
36913          * @param {Number} oldIndex
36914          * @param {Number} newIndex
36915          */
36916         "columnmove" : true,
36917         /**
36918          * @event startdrag
36919          * Fires when row(s) start being dragged
36920          * @param {Grid} this
36921          * @param {Roo.GridDD} dd The drag drop object
36922          * @param {event} e The raw browser event
36923          */
36924         "startdrag" : true,
36925         /**
36926          * @event enddrag
36927          * Fires when a drag operation is complete
36928          * @param {Grid} this
36929          * @param {Roo.GridDD} dd The drag drop object
36930          * @param {event} e The raw browser event
36931          */
36932         "enddrag" : true,
36933         /**
36934          * @event dragdrop
36935          * Fires when dragged row(s) are dropped on a valid DD target
36936          * @param {Grid} this
36937          * @param {Roo.GridDD} dd The drag drop object
36938          * @param {String} targetId The target drag drop object
36939          * @param {event} e The raw browser event
36940          */
36941         "dragdrop" : true,
36942         /**
36943          * @event dragover
36944          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
36945          * @param {Grid} this
36946          * @param {Roo.GridDD} dd The drag drop object
36947          * @param {String} targetId The target drag drop object
36948          * @param {event} e The raw browser event
36949          */
36950         "dragover" : true,
36951         /**
36952          * @event dragenter
36953          *  Fires when the dragged row(s) first cross another DD target while being dragged
36954          * @param {Grid} this
36955          * @param {Roo.GridDD} dd The drag drop object
36956          * @param {String} targetId The target drag drop object
36957          * @param {event} e The raw browser event
36958          */
36959         "dragenter" : true,
36960         /**
36961          * @event dragout
36962          * Fires when the dragged row(s) leave another DD target while being dragged
36963          * @param {Grid} this
36964          * @param {Roo.GridDD} dd The drag drop object
36965          * @param {String} targetId The target drag drop object
36966          * @param {event} e The raw browser event
36967          */
36968         "dragout" : true,
36969         /**
36970          * @event rowclass
36971          * Fires when a row is rendered, so you can change add a style to it.
36972          * @param {GridView} gridview   The grid view
36973          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
36974          */
36975         'rowclass' : true,
36976
36977         /**
36978          * @event render
36979          * Fires when the grid is rendered
36980          * @param {Grid} grid
36981          */
36982         'render' : true,
36983             /**
36984              * @event select
36985              * Fires when a date is selected
36986              * @param {DatePicker} this
36987              * @param {Date} date The selected date
36988              */
36989         'select': true,
36990         /**
36991              * @event monthchange
36992              * Fires when the displayed month changes 
36993              * @param {DatePicker} this
36994              * @param {Date} date The selected month
36995              */
36996         'monthchange': true,
36997         /**
36998              * @event evententer
36999              * Fires when mouse over an event
37000              * @param {Calendar} this
37001              * @param {event} Event
37002              */
37003         'evententer': true,
37004         /**
37005              * @event eventleave
37006              * Fires when the mouse leaves an
37007              * @param {Calendar} this
37008              * @param {event}
37009              */
37010         'eventleave': true,
37011         /**
37012              * @event eventclick
37013              * Fires when the mouse click an
37014              * @param {Calendar} this
37015              * @param {event}
37016              */
37017         'eventclick': true,
37018         /**
37019              * @event eventrender
37020              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
37021              * @param {Calendar} this
37022              * @param {data} data to be modified
37023              */
37024         'eventrender': true
37025         
37026     });
37027
37028     Roo.grid.Grid.superclass.constructor.call(this);
37029     this.on('render', function() {
37030         this.view.el.addClass('x-grid-cal'); 
37031         
37032         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
37033
37034     },this);
37035     
37036     if (!Roo.grid.Calendar.style) {
37037         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
37038             
37039             
37040             '.x-grid-cal .x-grid-col' :  {
37041                 height: 'auto !important',
37042                 'vertical-align': 'top'
37043             },
37044             '.x-grid-cal  .fc-event-hori' : {
37045                 height: '14px'
37046             }
37047              
37048             
37049         }, Roo.id());
37050     }
37051
37052     
37053     
37054 };
37055 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
37056     /**
37057      * @cfg {Store} eventStore The store that loads events.
37058      */
37059     eventStore : 25,
37060
37061      
37062     activeDate : false,
37063     startDay : 0,
37064     autoWidth : true,
37065     monitorWindowResize : false,
37066
37067     
37068     resizeColumns : function() {
37069         var col = (this.view.el.getWidth() / 7) - 3;
37070         // loop through cols, and setWidth
37071         for(var i =0 ; i < 7 ; i++){
37072             this.cm.setColumnWidth(i, col);
37073         }
37074     },
37075      setDate :function(date) {
37076         
37077         Roo.log('setDate?');
37078         
37079         this.resizeColumns();
37080         var vd = this.activeDate;
37081         this.activeDate = date;
37082 //        if(vd && this.el){
37083 //            var t = date.getTime();
37084 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
37085 //                Roo.log('using add remove');
37086 //                
37087 //                this.fireEvent('monthchange', this, date);
37088 //                
37089 //                this.cells.removeClass("fc-state-highlight");
37090 //                this.cells.each(function(c){
37091 //                   if(c.dateValue == t){
37092 //                       c.addClass("fc-state-highlight");
37093 //                       setTimeout(function(){
37094 //                            try{c.dom.firstChild.focus();}catch(e){}
37095 //                       }, 50);
37096 //                       return false;
37097 //                   }
37098 //                   return true;
37099 //                });
37100 //                return;
37101 //            }
37102 //        }
37103         
37104         var days = date.getDaysInMonth();
37105         
37106         var firstOfMonth = date.getFirstDateOfMonth();
37107         var startingPos = firstOfMonth.getDay()-this.startDay;
37108         
37109         if(startingPos < this.startDay){
37110             startingPos += 7;
37111         }
37112         
37113         var pm = date.add(Date.MONTH, -1);
37114         var prevStart = pm.getDaysInMonth()-startingPos;
37115 //        
37116         
37117         
37118         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37119         
37120         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
37121         //this.cells.addClassOnOver('fc-state-hover');
37122         
37123         var cells = this.cells.elements;
37124         var textEls = this.textNodes;
37125         
37126         //Roo.each(cells, function(cell){
37127         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
37128         //});
37129         
37130         days += startingPos;
37131
37132         // convert everything to numbers so it's fast
37133         var day = 86400000;
37134         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
37135         //Roo.log(d);
37136         //Roo.log(pm);
37137         //Roo.log(prevStart);
37138         
37139         var today = new Date().clearTime().getTime();
37140         var sel = date.clearTime().getTime();
37141         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
37142         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
37143         var ddMatch = this.disabledDatesRE;
37144         var ddText = this.disabledDatesText;
37145         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
37146         var ddaysText = this.disabledDaysText;
37147         var format = this.format;
37148         
37149         var setCellClass = function(cal, cell){
37150             
37151             //Roo.log('set Cell Class');
37152             cell.title = "";
37153             var t = d.getTime();
37154             
37155             //Roo.log(d);
37156             
37157             
37158             cell.dateValue = t;
37159             if(t == today){
37160                 cell.className += " fc-today";
37161                 cell.className += " fc-state-highlight";
37162                 cell.title = cal.todayText;
37163             }
37164             if(t == sel){
37165                 // disable highlight in other month..
37166                 cell.className += " fc-state-highlight";
37167                 
37168             }
37169             // disabling
37170             if(t < min) {
37171                 //cell.className = " fc-state-disabled";
37172                 cell.title = cal.minText;
37173                 return;
37174             }
37175             if(t > max) {
37176                 //cell.className = " fc-state-disabled";
37177                 cell.title = cal.maxText;
37178                 return;
37179             }
37180             if(ddays){
37181                 if(ddays.indexOf(d.getDay()) != -1){
37182                     // cell.title = ddaysText;
37183                    // cell.className = " fc-state-disabled";
37184                 }
37185             }
37186             if(ddMatch && format){
37187                 var fvalue = d.dateFormat(format);
37188                 if(ddMatch.test(fvalue)){
37189                     cell.title = ddText.replace("%0", fvalue);
37190                    cell.className = " fc-state-disabled";
37191                 }
37192             }
37193             
37194             if (!cell.initialClassName) {
37195                 cell.initialClassName = cell.dom.className;
37196             }
37197             
37198             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
37199         };
37200
37201         var i = 0;
37202         
37203         for(; i < startingPos; i++) {
37204             cells[i].dayName =  (++prevStart);
37205             Roo.log(textEls[i]);
37206             d.setDate(d.getDate()+1);
37207             
37208             //cells[i].className = "fc-past fc-other-month";
37209             setCellClass(this, cells[i]);
37210         }
37211         
37212         var intDay = 0;
37213         
37214         for(; i < days; i++){
37215             intDay = i - startingPos + 1;
37216             cells[i].dayName =  (intDay);
37217             d.setDate(d.getDate()+1);
37218             
37219             cells[i].className = ''; // "x-date-active";
37220             setCellClass(this, cells[i]);
37221         }
37222         var extraDays = 0;
37223         
37224         for(; i < 42; i++) {
37225             //textEls[i].innerHTML = (++extraDays);
37226             
37227             d.setDate(d.getDate()+1);
37228             cells[i].dayName = (++extraDays);
37229             cells[i].className = "fc-future fc-other-month";
37230             setCellClass(this, cells[i]);
37231         }
37232         
37233         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
37234         
37235         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
37236         
37237         // this will cause all the cells to mis
37238         var rows= [];
37239         var i =0;
37240         for (var r = 0;r < 6;r++) {
37241             for (var c =0;c < 7;c++) {
37242                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
37243             }    
37244         }
37245         
37246         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37247         for(i=0;i<cells.length;i++) {
37248             
37249             this.cells.elements[i].dayName = cells[i].dayName ;
37250             this.cells.elements[i].className = cells[i].className;
37251             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
37252             this.cells.elements[i].title = cells[i].title ;
37253             this.cells.elements[i].dateValue = cells[i].dateValue ;
37254         }
37255         
37256         
37257         
37258         
37259         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
37260         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
37261         
37262         ////if(totalRows != 6){
37263             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
37264            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
37265        // }
37266         
37267         this.fireEvent('monthchange', this, date);
37268         
37269         
37270     },
37271  /**
37272      * Returns the grid's SelectionModel.
37273      * @return {SelectionModel}
37274      */
37275     getSelectionModel : function(){
37276         if(!this.selModel){
37277             this.selModel = new Roo.grid.CellSelectionModel();
37278         }
37279         return this.selModel;
37280     },
37281
37282     load: function() {
37283         this.eventStore.load()
37284         
37285         
37286         
37287     },
37288     
37289     findCell : function(dt) {
37290         dt = dt.clearTime().getTime();
37291         var ret = false;
37292         this.cells.each(function(c){
37293             //Roo.log("check " +c.dateValue + '?=' + dt);
37294             if(c.dateValue == dt){
37295                 ret = c;
37296                 return false;
37297             }
37298             return true;
37299         });
37300         
37301         return ret;
37302     },
37303     
37304     findCells : function(rec) {
37305         var s = rec.data.start_dt.clone().clearTime().getTime();
37306        // Roo.log(s);
37307         var e= rec.data.end_dt.clone().clearTime().getTime();
37308        // Roo.log(e);
37309         var ret = [];
37310         this.cells.each(function(c){
37311              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
37312             
37313             if(c.dateValue > e){
37314                 return ;
37315             }
37316             if(c.dateValue < s){
37317                 return ;
37318             }
37319             ret.push(c);
37320         });
37321         
37322         return ret;    
37323     },
37324     
37325     findBestRow: function(cells)
37326     {
37327         var ret = 0;
37328         
37329         for (var i =0 ; i < cells.length;i++) {
37330             ret  = Math.max(cells[i].rows || 0,ret);
37331         }
37332         return ret;
37333         
37334     },
37335     
37336     
37337     addItem : function(rec)
37338     {
37339         // look for vertical location slot in
37340         var cells = this.findCells(rec);
37341         
37342         rec.row = this.findBestRow(cells);
37343         
37344         // work out the location.
37345         
37346         var crow = false;
37347         var rows = [];
37348         for(var i =0; i < cells.length; i++) {
37349             if (!crow) {
37350                 crow = {
37351                     start : cells[i],
37352                     end :  cells[i]
37353                 };
37354                 continue;
37355             }
37356             if (crow.start.getY() == cells[i].getY()) {
37357                 // on same row.
37358                 crow.end = cells[i];
37359                 continue;
37360             }
37361             // different row.
37362             rows.push(crow);
37363             crow = {
37364                 start: cells[i],
37365                 end : cells[i]
37366             };
37367             
37368         }
37369         
37370         rows.push(crow);
37371         rec.els = [];
37372         rec.rows = rows;
37373         rec.cells = cells;
37374         for (var i = 0; i < cells.length;i++) {
37375             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
37376             
37377         }
37378         
37379         
37380     },
37381     
37382     clearEvents: function() {
37383         
37384         if (!this.eventStore.getCount()) {
37385             return;
37386         }
37387         // reset number of rows in cells.
37388         Roo.each(this.cells.elements, function(c){
37389             c.rows = 0;
37390         });
37391         
37392         this.eventStore.each(function(e) {
37393             this.clearEvent(e);
37394         },this);
37395         
37396     },
37397     
37398     clearEvent : function(ev)
37399     {
37400         if (ev.els) {
37401             Roo.each(ev.els, function(el) {
37402                 el.un('mouseenter' ,this.onEventEnter, this);
37403                 el.un('mouseleave' ,this.onEventLeave, this);
37404                 el.remove();
37405             },this);
37406             ev.els = [];
37407         }
37408     },
37409     
37410     
37411     renderEvent : function(ev,ctr) {
37412         if (!ctr) {
37413              ctr = this.view.el.select('.fc-event-container',true).first();
37414         }
37415         
37416          
37417         this.clearEvent(ev);
37418             //code
37419        
37420         
37421         
37422         ev.els = [];
37423         var cells = ev.cells;
37424         var rows = ev.rows;
37425         this.fireEvent('eventrender', this, ev);
37426         
37427         for(var i =0; i < rows.length; i++) {
37428             
37429             cls = '';
37430             if (i == 0) {
37431                 cls += ' fc-event-start';
37432             }
37433             if ((i+1) == rows.length) {
37434                 cls += ' fc-event-end';
37435             }
37436             
37437             //Roo.log(ev.data);
37438             // how many rows should it span..
37439             var cg = this.eventTmpl.append(ctr,Roo.apply({
37440                 fccls : cls
37441                 
37442             }, ev.data) , true);
37443             
37444             
37445             cg.on('mouseenter' ,this.onEventEnter, this, ev);
37446             cg.on('mouseleave' ,this.onEventLeave, this, ev);
37447             cg.on('click', this.onEventClick, this, ev);
37448             
37449             ev.els.push(cg);
37450             
37451             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
37452             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
37453             //Roo.log(cg);
37454              
37455             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
37456             cg.setWidth(ebox.right - sbox.x -2);
37457         }
37458     },
37459     
37460     renderEvents: function()
37461     {   
37462         // first make sure there is enough space..
37463         
37464         if (!this.eventTmpl) {
37465             this.eventTmpl = new Roo.Template(
37466                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
37467                     '<div class="fc-event-inner">' +
37468                         '<span class="fc-event-time">{time}</span>' +
37469                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
37470                     '</div>' +
37471                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
37472                 '</div>'
37473             );
37474                 
37475         }
37476                
37477         
37478         
37479         this.cells.each(function(c) {
37480             //Roo.log(c.select('.fc-day-content div',true).first());
37481             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
37482         });
37483         
37484         var ctr = this.view.el.select('.fc-event-container',true).first();
37485         
37486         var cls;
37487         this.eventStore.each(function(ev){
37488             
37489             this.renderEvent(ev);
37490              
37491              
37492         }, this);
37493         this.view.layout();
37494         
37495     },
37496     
37497     onEventEnter: function (e, el,event,d) {
37498         this.fireEvent('evententer', this, el, event);
37499     },
37500     
37501     onEventLeave: function (e, el,event,d) {
37502         this.fireEvent('eventleave', this, el, event);
37503     },
37504     
37505     onEventClick: function (e, el,event,d) {
37506         this.fireEvent('eventclick', this, el, event);
37507     },
37508     
37509     onMonthChange: function () {
37510         this.store.load();
37511     },
37512     
37513     onLoad: function () {
37514         
37515         //Roo.log('calendar onload');
37516 //         
37517         if(this.eventStore.getCount() > 0){
37518             
37519            
37520             
37521             this.eventStore.each(function(d){
37522                 
37523                 
37524                 // FIXME..
37525                 var add =   d.data;
37526                 if (typeof(add.end_dt) == 'undefined')  {
37527                     Roo.log("Missing End time in calendar data: ");
37528                     Roo.log(d);
37529                     return;
37530                 }
37531                 if (typeof(add.start_dt) == 'undefined')  {
37532                     Roo.log("Missing Start time in calendar data: ");
37533                     Roo.log(d);
37534                     return;
37535                 }
37536                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
37537                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
37538                 add.id = add.id || d.id;
37539                 add.title = add.title || '??';
37540                 
37541                 this.addItem(d);
37542                 
37543              
37544             },this);
37545         }
37546         
37547         this.renderEvents();
37548     }
37549     
37550
37551 });
37552 /*
37553  grid : {
37554                 xtype: 'Grid',
37555                 xns: Roo.grid,
37556                 listeners : {
37557                     render : function ()
37558                     {
37559                         _this.grid = this;
37560                         
37561                         if (!this.view.el.hasClass('course-timesheet')) {
37562                             this.view.el.addClass('course-timesheet');
37563                         }
37564                         if (this.tsStyle) {
37565                             this.ds.load({});
37566                             return; 
37567                         }
37568                         Roo.log('width');
37569                         Roo.log(_this.grid.view.el.getWidth());
37570                         
37571                         
37572                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
37573                             '.course-timesheet .x-grid-row' : {
37574                                 height: '80px'
37575                             },
37576                             '.x-grid-row td' : {
37577                                 'vertical-align' : 0
37578                             },
37579                             '.course-edit-link' : {
37580                                 'color' : 'blue',
37581                                 'text-overflow' : 'ellipsis',
37582                                 'overflow' : 'hidden',
37583                                 'white-space' : 'nowrap',
37584                                 'cursor' : 'pointer'
37585                             },
37586                             '.sub-link' : {
37587                                 'color' : 'green'
37588                             },
37589                             '.de-act-sup-link' : {
37590                                 'color' : 'purple',
37591                                 'text-decoration' : 'line-through'
37592                             },
37593                             '.de-act-link' : {
37594                                 'color' : 'red',
37595                                 'text-decoration' : 'line-through'
37596                             },
37597                             '.course-timesheet .course-highlight' : {
37598                                 'border-top-style': 'dashed !important',
37599                                 'border-bottom-bottom': 'dashed !important'
37600                             },
37601                             '.course-timesheet .course-item' : {
37602                                 'font-family'   : 'tahoma, arial, helvetica',
37603                                 'font-size'     : '11px',
37604                                 'overflow'      : 'hidden',
37605                                 'padding-left'  : '10px',
37606                                 'padding-right' : '10px',
37607                                 'padding-top' : '10px' 
37608                             }
37609                             
37610                         }, Roo.id());
37611                                 this.ds.load({});
37612                     }
37613                 },
37614                 autoWidth : true,
37615                 monitorWindowResize : false,
37616                 cellrenderer : function(v,x,r)
37617                 {
37618                     return v;
37619                 },
37620                 sm : {
37621                     xtype: 'CellSelectionModel',
37622                     xns: Roo.grid
37623                 },
37624                 dataSource : {
37625                     xtype: 'Store',
37626                     xns: Roo.data,
37627                     listeners : {
37628                         beforeload : function (_self, options)
37629                         {
37630                             options.params = options.params || {};
37631                             options.params._month = _this.monthField.getValue();
37632                             options.params.limit = 9999;
37633                             options.params['sort'] = 'when_dt';    
37634                             options.params['dir'] = 'ASC';    
37635                             this.proxy.loadResponse = this.loadResponse;
37636                             Roo.log("load?");
37637                             //this.addColumns();
37638                         },
37639                         load : function (_self, records, options)
37640                         {
37641                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
37642                                 // if you click on the translation.. you can edit it...
37643                                 var el = Roo.get(this);
37644                                 var id = el.dom.getAttribute('data-id');
37645                                 var d = el.dom.getAttribute('data-date');
37646                                 var t = el.dom.getAttribute('data-time');
37647                                 //var id = this.child('span').dom.textContent;
37648                                 
37649                                 //Roo.log(this);
37650                                 Pman.Dialog.CourseCalendar.show({
37651                                     id : id,
37652                                     when_d : d,
37653                                     when_t : t,
37654                                     productitem_active : id ? 1 : 0
37655                                 }, function() {
37656                                     _this.grid.ds.load({});
37657                                 });
37658                            
37659                            });
37660                            
37661                            _this.panel.fireEvent('resize', [ '', '' ]);
37662                         }
37663                     },
37664                     loadResponse : function(o, success, response){
37665                             // this is overridden on before load..
37666                             
37667                             Roo.log("our code?");       
37668                             //Roo.log(success);
37669                             //Roo.log(response)
37670                             delete this.activeRequest;
37671                             if(!success){
37672                                 this.fireEvent("loadexception", this, o, response);
37673                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
37674                                 return;
37675                             }
37676                             var result;
37677                             try {
37678                                 result = o.reader.read(response);
37679                             }catch(e){
37680                                 Roo.log("load exception?");
37681                                 this.fireEvent("loadexception", this, o, response, e);
37682                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
37683                                 return;
37684                             }
37685                             Roo.log("ready...");        
37686                             // loop through result.records;
37687                             // and set this.tdate[date] = [] << array of records..
37688                             _this.tdata  = {};
37689                             Roo.each(result.records, function(r){
37690                                 //Roo.log(r.data);
37691                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
37692                                     _this.tdata[r.data.when_dt.format('j')] = [];
37693                                 }
37694                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
37695                             });
37696                             
37697                             //Roo.log(_this.tdata);
37698                             
37699                             result.records = [];
37700                             result.totalRecords = 6;
37701                     
37702                             // let's generate some duumy records for the rows.
37703                             //var st = _this.dateField.getValue();
37704                             
37705                             // work out monday..
37706                             //st = st.add(Date.DAY, -1 * st.format('w'));
37707                             
37708                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
37709                             
37710                             var firstOfMonth = date.getFirstDayOfMonth();
37711                             var days = date.getDaysInMonth();
37712                             var d = 1;
37713                             var firstAdded = false;
37714                             for (var i = 0; i < result.totalRecords ; i++) {
37715                                 //var d= st.add(Date.DAY, i);
37716                                 var row = {};
37717                                 var added = 0;
37718                                 for(var w = 0 ; w < 7 ; w++){
37719                                     if(!firstAdded && firstOfMonth != w){
37720                                         continue;
37721                                     }
37722                                     if(d > days){
37723                                         continue;
37724                                     }
37725                                     firstAdded = true;
37726                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
37727                                     row['weekday'+w] = String.format(
37728                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
37729                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
37730                                                     d,
37731                                                     date.format('Y-m-')+dd
37732                                                 );
37733                                     added++;
37734                                     if(typeof(_this.tdata[d]) != 'undefined'){
37735                                         Roo.each(_this.tdata[d], function(r){
37736                                             var is_sub = '';
37737                                             var deactive = '';
37738                                             var id = r.id;
37739                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
37740                                             if(r.parent_id*1>0){
37741                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
37742                                                 id = r.parent_id;
37743                                             }
37744                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
37745                                                 deactive = 'de-act-link';
37746                                             }
37747                                             
37748                                             row['weekday'+w] += String.format(
37749                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
37750                                                     id, //0
37751                                                     r.product_id_name, //1
37752                                                     r.when_dt.format('h:ia'), //2
37753                                                     is_sub, //3
37754                                                     deactive, //4
37755                                                     desc // 5
37756                                             );
37757                                         });
37758                                     }
37759                                     d++;
37760                                 }
37761                                 
37762                                 // only do this if something added..
37763                                 if(added > 0){ 
37764                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
37765                                 }
37766                                 
37767                                 
37768                                 // push it twice. (second one with an hour..
37769                                 
37770                             }
37771                             //Roo.log(result);
37772                             this.fireEvent("load", this, o, o.request.arg);
37773                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
37774                         },
37775                     sortInfo : {field: 'when_dt', direction : 'ASC' },
37776                     proxy : {
37777                         xtype: 'HttpProxy',
37778                         xns: Roo.data,
37779                         method : 'GET',
37780                         url : baseURL + '/Roo/Shop_course.php'
37781                     },
37782                     reader : {
37783                         xtype: 'JsonReader',
37784                         xns: Roo.data,
37785                         id : 'id',
37786                         fields : [
37787                             {
37788                                 'name': 'id',
37789                                 'type': 'int'
37790                             },
37791                             {
37792                                 'name': 'when_dt',
37793                                 'type': 'string'
37794                             },
37795                             {
37796                                 'name': 'end_dt',
37797                                 'type': 'string'
37798                             },
37799                             {
37800                                 'name': 'parent_id',
37801                                 'type': 'int'
37802                             },
37803                             {
37804                                 'name': 'product_id',
37805                                 'type': 'int'
37806                             },
37807                             {
37808                                 'name': 'productitem_id',
37809                                 'type': 'int'
37810                             },
37811                             {
37812                                 'name': 'guid',
37813                                 'type': 'int'
37814                             }
37815                         ]
37816                     }
37817                 },
37818                 toolbar : {
37819                     xtype: 'Toolbar',
37820                     xns: Roo,
37821                     items : [
37822                         {
37823                             xtype: 'Button',
37824                             xns: Roo.Toolbar,
37825                             listeners : {
37826                                 click : function (_self, e)
37827                                 {
37828                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
37829                                     sd.setMonth(sd.getMonth()-1);
37830                                     _this.monthField.setValue(sd.format('Y-m-d'));
37831                                     _this.grid.ds.load({});
37832                                 }
37833                             },
37834                             text : "Back"
37835                         },
37836                         {
37837                             xtype: 'Separator',
37838                             xns: Roo.Toolbar
37839                         },
37840                         {
37841                             xtype: 'MonthField',
37842                             xns: Roo.form,
37843                             listeners : {
37844                                 render : function (_self)
37845                                 {
37846                                     _this.monthField = _self;
37847                                    // _this.monthField.set  today
37848                                 },
37849                                 select : function (combo, date)
37850                                 {
37851                                     _this.grid.ds.load({});
37852                                 }
37853                             },
37854                             value : (function() { return new Date(); })()
37855                         },
37856                         {
37857                             xtype: 'Separator',
37858                             xns: Roo.Toolbar
37859                         },
37860                         {
37861                             xtype: 'TextItem',
37862                             xns: Roo.Toolbar,
37863                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
37864                         },
37865                         {
37866                             xtype: 'Fill',
37867                             xns: Roo.Toolbar
37868                         },
37869                         {
37870                             xtype: 'Button',
37871                             xns: Roo.Toolbar,
37872                             listeners : {
37873                                 click : function (_self, e)
37874                                 {
37875                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
37876                                     sd.setMonth(sd.getMonth()+1);
37877                                     _this.monthField.setValue(sd.format('Y-m-d'));
37878                                     _this.grid.ds.load({});
37879                                 }
37880                             },
37881                             text : "Next"
37882                         }
37883                     ]
37884                 },
37885                  
37886             }
37887         };
37888         
37889         *//*
37890  * Based on:
37891  * Ext JS Library 1.1.1
37892  * Copyright(c) 2006-2007, Ext JS, LLC.
37893  *
37894  * Originally Released Under LGPL - original licence link has changed is not relivant.
37895  *
37896  * Fork - LGPL
37897  * <script type="text/javascript">
37898  */
37899  
37900 /**
37901  * @class Roo.LoadMask
37902  * A simple utility class for generically masking elements while loading data.  If the element being masked has
37903  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
37904  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
37905  * element's UpdateManager load indicator and will be destroyed after the initial load.
37906  * @constructor
37907  * Create a new LoadMask
37908  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
37909  * @param {Object} config The config object
37910  */
37911 Roo.LoadMask = function(el, config){
37912     this.el = Roo.get(el);
37913     Roo.apply(this, config);
37914     if(this.store){
37915         this.store.on('beforeload', this.onBeforeLoad, this);
37916         this.store.on('load', this.onLoad, this);
37917         this.store.on('loadexception', this.onLoadException, this);
37918         this.removeMask = false;
37919     }else{
37920         var um = this.el.getUpdateManager();
37921         um.showLoadIndicator = false; // disable the default indicator
37922         um.on('beforeupdate', this.onBeforeLoad, this);
37923         um.on('update', this.onLoad, this);
37924         um.on('failure', this.onLoad, this);
37925         this.removeMask = true;
37926     }
37927 };
37928
37929 Roo.LoadMask.prototype = {
37930     /**
37931      * @cfg {Boolean} removeMask
37932      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
37933      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
37934      */
37935     /**
37936      * @cfg {String} msg
37937      * The text to display in a centered loading message box (defaults to 'Loading...')
37938      */
37939     msg : 'Loading...',
37940     /**
37941      * @cfg {String} msgCls
37942      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
37943      */
37944     msgCls : 'x-mask-loading',
37945
37946     /**
37947      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
37948      * @type Boolean
37949      */
37950     disabled: false,
37951
37952     /**
37953      * Disables the mask to prevent it from being displayed
37954      */
37955     disable : function(){
37956        this.disabled = true;
37957     },
37958
37959     /**
37960      * Enables the mask so that it can be displayed
37961      */
37962     enable : function(){
37963         this.disabled = false;
37964     },
37965     
37966     onLoadException : function()
37967     {
37968         Roo.log(arguments);
37969         
37970         if (typeof(arguments[3]) != 'undefined') {
37971             Roo.MessageBox.alert("Error loading",arguments[3]);
37972         } 
37973         /*
37974         try {
37975             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
37976                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
37977             }   
37978         } catch(e) {
37979             
37980         }
37981         */
37982     
37983         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
37984     },
37985     // private
37986     onLoad : function()
37987     {
37988         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
37989     },
37990
37991     // private
37992     onBeforeLoad : function(){
37993         if(!this.disabled){
37994             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
37995         }
37996     },
37997
37998     // private
37999     destroy : function(){
38000         if(this.store){
38001             this.store.un('beforeload', this.onBeforeLoad, this);
38002             this.store.un('load', this.onLoad, this);
38003             this.store.un('loadexception', this.onLoadException, this);
38004         }else{
38005             var um = this.el.getUpdateManager();
38006             um.un('beforeupdate', this.onBeforeLoad, this);
38007             um.un('update', this.onLoad, this);
38008             um.un('failure', this.onLoad, this);
38009         }
38010     }
38011 };/*
38012  * Based on:
38013  * Ext JS Library 1.1.1
38014  * Copyright(c) 2006-2007, Ext JS, LLC.
38015  *
38016  * Originally Released Under LGPL - original licence link has changed is not relivant.
38017  *
38018  * Fork - LGPL
38019  * <script type="text/javascript">
38020  */
38021
38022
38023 /**
38024  * @class Roo.XTemplate
38025  * @extends Roo.Template
38026  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
38027 <pre><code>
38028 var t = new Roo.XTemplate(
38029         '&lt;select name="{name}"&gt;',
38030                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
38031         '&lt;/select&gt;'
38032 );
38033  
38034 // then append, applying the master template values
38035  </code></pre>
38036  *
38037  * Supported features:
38038  *
38039  *  Tags:
38040
38041 <pre><code>
38042       {a_variable} - output encoded.
38043       {a_variable.format:("Y-m-d")} - call a method on the variable
38044       {a_variable:raw} - unencoded output
38045       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
38046       {a_variable:this.method_on_template(...)} - call a method on the template object.
38047  
38048 </code></pre>
38049  *  The tpl tag:
38050 <pre><code>
38051         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
38052         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
38053         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
38054         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
38055   
38056         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
38057         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
38058 </code></pre>
38059  *      
38060  */
38061 Roo.XTemplate = function()
38062 {
38063     Roo.XTemplate.superclass.constructor.apply(this, arguments);
38064     if (this.html) {
38065         this.compile();
38066     }
38067 };
38068
38069
38070 Roo.extend(Roo.XTemplate, Roo.Template, {
38071
38072     /**
38073      * The various sub templates
38074      */
38075     tpls : false,
38076     /**
38077      *
38078      * basic tag replacing syntax
38079      * WORD:WORD()
38080      *
38081      * // you can fake an object call by doing this
38082      *  x.t:(test,tesT) 
38083      * 
38084      */
38085     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
38086
38087     /**
38088      * compile the template
38089      *
38090      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
38091      *
38092      */
38093     compile: function()
38094     {
38095         var s = this.html;
38096      
38097         s = ['<tpl>', s, '</tpl>'].join('');
38098     
38099         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
38100             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
38101             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
38102             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
38103             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
38104             m,
38105             id     = 0,
38106             tpls   = [];
38107     
38108         while(true == !!(m = s.match(re))){
38109             var forMatch   = m[0].match(nameRe),
38110                 ifMatch   = m[0].match(ifRe),
38111                 execMatch   = m[0].match(execRe),
38112                 namedMatch   = m[0].match(namedRe),
38113                 
38114                 exp  = null, 
38115                 fn   = null,
38116                 exec = null,
38117                 name = forMatch && forMatch[1] ? forMatch[1] : '';
38118                 
38119             if (ifMatch) {
38120                 // if - puts fn into test..
38121                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
38122                 if(exp){
38123                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
38124                 }
38125             }
38126             
38127             if (execMatch) {
38128                 // exec - calls a function... returns empty if true is  returned.
38129                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
38130                 if(exp){
38131                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
38132                 }
38133             }
38134             
38135             
38136             if (name) {
38137                 // for = 
38138                 switch(name){
38139                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
38140                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
38141                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
38142                 }
38143             }
38144             var uid = namedMatch ? namedMatch[1] : id;
38145             
38146             
38147             tpls.push({
38148                 id:     namedMatch ? namedMatch[1] : id,
38149                 target: name,
38150                 exec:   exec,
38151                 test:   fn,
38152                 body:   m[1] || ''
38153             });
38154             if (namedMatch) {
38155                 s = s.replace(m[0], '');
38156             } else { 
38157                 s = s.replace(m[0], '{xtpl'+ id + '}');
38158             }
38159             ++id;
38160         }
38161         this.tpls = [];
38162         for(var i = tpls.length-1; i >= 0; --i){
38163             this.compileTpl(tpls[i]);
38164             this.tpls[tpls[i].id] = tpls[i];
38165         }
38166         this.master = tpls[tpls.length-1];
38167         return this;
38168     },
38169     /**
38170      * same as applyTemplate, except it's done to one of the subTemplates
38171      * when using named templates, you can do:
38172      *
38173      * var str = pl.applySubTemplate('your-name', values);
38174      *
38175      * 
38176      * @param {Number} id of the template
38177      * @param {Object} values to apply to template
38178      * @param {Object} parent (normaly the instance of this object)
38179      */
38180     applySubTemplate : function(id, values, parent)
38181     {
38182         
38183         
38184         var t = this.tpls[id];
38185         
38186         
38187         try { 
38188             if(t.test && !t.test.call(this, values, parent)){
38189                 return '';
38190             }
38191         } catch(e) {
38192             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
38193             Roo.log(e.toString());
38194             Roo.log(t.test);
38195             return ''
38196         }
38197         try { 
38198             
38199             if(t.exec && t.exec.call(this, values, parent)){
38200                 return '';
38201             }
38202         } catch(e) {
38203             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
38204             Roo.log(e.toString());
38205             Roo.log(t.exec);
38206             return ''
38207         }
38208         try {
38209             var vs = t.target ? t.target.call(this, values, parent) : values;
38210             parent = t.target ? values : parent;
38211             if(t.target && vs instanceof Array){
38212                 var buf = [];
38213                 for(var i = 0, len = vs.length; i < len; i++){
38214                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
38215                 }
38216                 return buf.join('');
38217             }
38218             return t.compiled.call(this, vs, parent);
38219         } catch (e) {
38220             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
38221             Roo.log(e.toString());
38222             Roo.log(t.compiled);
38223             return '';
38224         }
38225     },
38226
38227     compileTpl : function(tpl)
38228     {
38229         var fm = Roo.util.Format;
38230         var useF = this.disableFormats !== true;
38231         var sep = Roo.isGecko ? "+" : ",";
38232         var undef = function(str) {
38233             Roo.log("Property not found :"  + str);
38234             return '';
38235         };
38236         
38237         var fn = function(m, name, format, args)
38238         {
38239             //Roo.log(arguments);
38240             args = args ? args.replace(/\\'/g,"'") : args;
38241             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
38242             if (typeof(format) == 'undefined') {
38243                 format= 'htmlEncode';
38244             }
38245             if (format == 'raw' ) {
38246                 format = false;
38247             }
38248             
38249             if(name.substr(0, 4) == 'xtpl'){
38250                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
38251             }
38252             
38253             // build an array of options to determine if value is undefined..
38254             
38255             // basically get 'xxxx.yyyy' then do
38256             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
38257             //    (function () { Roo.log("Property not found"); return ''; })() :
38258             //    ......
38259             
38260             var udef_ar = [];
38261             var lookfor = '';
38262             Roo.each(name.split('.'), function(st) {
38263                 lookfor += (lookfor.length ? '.': '') + st;
38264                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
38265             });
38266             
38267             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
38268             
38269             
38270             if(format && useF){
38271                 
38272                 args = args ? ',' + args : "";
38273                  
38274                 if(format.substr(0, 5) != "this."){
38275                     format = "fm." + format + '(';
38276                 }else{
38277                     format = 'this.call("'+ format.substr(5) + '", ';
38278                     args = ", values";
38279                 }
38280                 
38281                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
38282             }
38283              
38284             if (args.length) {
38285                 // called with xxyx.yuu:(test,test)
38286                 // change to ()
38287                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
38288             }
38289             // raw.. - :raw modifier..
38290             return "'"+ sep + udef_st  + name + ")"+sep+"'";
38291             
38292         };
38293         var body;
38294         // branched to use + in gecko and [].join() in others
38295         if(Roo.isGecko){
38296             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
38297                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
38298                     "';};};";
38299         }else{
38300             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
38301             body.push(tpl.body.replace(/(\r\n|\n)/g,
38302                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
38303             body.push("'].join('');};};");
38304             body = body.join('');
38305         }
38306         
38307         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
38308        
38309         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
38310         eval(body);
38311         
38312         return this;
38313     },
38314
38315     applyTemplate : function(values){
38316         return this.master.compiled.call(this, values, {});
38317         //var s = this.subs;
38318     },
38319
38320     apply : function(){
38321         return this.applyTemplate.apply(this, arguments);
38322     }
38323
38324  });
38325
38326 Roo.XTemplate.from = function(el){
38327     el = Roo.getDom(el);
38328     return new Roo.XTemplate(el.value || el.innerHTML);
38329 };