roojs-ui.js
[roojs1] / roojs-ui-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11
12
13 /**
14  * @class Roo.data.SortTypes
15  * @singleton
16  * Defines the default sorting (casting?) comparison functions used when sorting data.
17  */
18 Roo.data.SortTypes = {
19     /**
20      * Default sort that does nothing
21      * @param {Mixed} s The value being converted
22      * @return {Mixed} The comparison value
23      */
24     none : function(s){
25         return s;
26     },
27     
28     /**
29      * The regular expression used to strip tags
30      * @type {RegExp}
31      * @property
32      */
33     stripTagsRE : /<\/?[^>]+>/gi,
34     
35     /**
36      * Strips all HTML tags to sort on text only
37      * @param {Mixed} s The value being converted
38      * @return {String} The comparison value
39      */
40     asText : function(s){
41         return String(s).replace(this.stripTagsRE, "");
42     },
43     
44     /**
45      * Strips all HTML tags to sort on text only - Case insensitive
46      * @param {Mixed} s The value being converted
47      * @return {String} The comparison value
48      */
49     asUCText : function(s){
50         return String(s).toUpperCase().replace(this.stripTagsRE, "");
51     },
52     
53     /**
54      * Case insensitive string
55      * @param {Mixed} s The value being converted
56      * @return {String} The comparison value
57      */
58     asUCString : function(s) {
59         return String(s).toUpperCase();
60     },
61     
62     /**
63      * Date sorting
64      * @param {Mixed} s The value being converted
65      * @return {Number} The comparison value
66      */
67     asDate : function(s) {
68         if(!s){
69             return 0;
70         }
71         if(s instanceof Date){
72             return s.getTime();
73         }
74         return Date.parse(String(s));
75     },
76     
77     /**
78      * Float sorting
79      * @param {Mixed} s The value being converted
80      * @return {Float} The comparison value
81      */
82     asFloat : function(s) {
83         var val = parseFloat(String(s).replace(/,/g, ""));
84         if(isNaN(val)) {
85             val = 0;
86         }
87         return val;
88     },
89     
90     /**
91      * Integer sorting
92      * @param {Mixed} s The value being converted
93      * @return {Number} The comparison value
94      */
95     asInt : function(s) {
96         var val = parseInt(String(s).replace(/,/g, ""));
97         if(isNaN(val)) {
98             val = 0;
99         }
100         return val;
101     }
102 };/*
103  * Based on:
104  * Ext JS Library 1.1.1
105  * Copyright(c) 2006-2007, Ext JS, LLC.
106  *
107  * Originally Released Under LGPL - original licence link has changed is not relivant.
108  *
109  * Fork - LGPL
110  * <script type="text/javascript">
111  */
112
113 /**
114 * @class Roo.data.Record
115  * Instances of this class encapsulate both record <em>definition</em> information, and record
116  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
117  * to access Records cached in an {@link Roo.data.Store} object.<br>
118  * <p>
119  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
120  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
121  * objects.<br>
122  * <p>
123  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
124  * @constructor
125  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
126  * {@link #create}. The parameters are the same.
127  * @param {Array} data An associative Array of data values keyed by the field name.
128  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
129  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
130  * not specified an integer id is generated.
131  */
132 Roo.data.Record = function(data, id){
133     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
134     this.data = data;
135 };
136
137 /**
138  * Generate a constructor for a specific record layout.
139  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
140  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
141  * Each field definition object may contain the following properties: <ul>
142  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
143  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
144  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
145  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
146  * is being used, then this is a string containing the javascript expression to reference the data relative to 
147  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
148  * to the data item relative to the record element. If the mapping expression is the same as the field name,
149  * this may be omitted.</p></li>
150  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
151  * <ul><li>auto (Default, implies no conversion)</li>
152  * <li>string</li>
153  * <li>int</li>
154  * <li>float</li>
155  * <li>boolean</li>
156  * <li>date</li></ul></p></li>
157  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
158  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
159  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
160  * by the Reader into an object that will be stored in the Record. It is passed the
161  * following parameters:<ul>
162  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
163  * </ul></p></li>
164  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
165  * </ul>
166  * <br>usage:<br><pre><code>
167 var TopicRecord = Roo.data.Record.create(
168     {name: 'title', mapping: 'topic_title'},
169     {name: 'author', mapping: 'username'},
170     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
171     {name: 'lastPost', mapping: 'post_time', type: 'date'},
172     {name: 'lastPoster', mapping: 'user2'},
173     {name: 'excerpt', mapping: 'post_text'}
174 );
175
176 var myNewRecord = new TopicRecord({
177     title: 'Do my job please',
178     author: 'noobie',
179     totalPosts: 1,
180     lastPost: new Date(),
181     lastPoster: 'Animal',
182     excerpt: 'No way dude!'
183 });
184 myStore.add(myNewRecord);
185 </code></pre>
186  * @method create
187  * @static
188  */
189 Roo.data.Record.create = function(o){
190     var f = function(){
191         f.superclass.constructor.apply(this, arguments);
192     };
193     Roo.extend(f, Roo.data.Record);
194     var p = f.prototype;
195     p.fields = new Roo.util.MixedCollection(false, function(field){
196         return field.name;
197     });
198     for(var i = 0, len = o.length; i < len; i++){
199         p.fields.add(new Roo.data.Field(o[i]));
200     }
201     f.getField = function(name){
202         return p.fields.get(name);  
203     };
204     return f;
205 };
206
207 Roo.data.Record.AUTO_ID = 1000;
208 Roo.data.Record.EDIT = 'edit';
209 Roo.data.Record.REJECT = 'reject';
210 Roo.data.Record.COMMIT = 'commit';
211
212 Roo.data.Record.prototype = {
213     /**
214      * Readonly flag - true if this record has been modified.
215      * @type Boolean
216      */
217     dirty : false,
218     editing : false,
219     error: null,
220     modified: null,
221
222     // private
223     join : function(store){
224         this.store = store;
225     },
226
227     /**
228      * Set the named field to the specified value.
229      * @param {String} name The name of the field to set.
230      * @param {Object} value The value to set the field to.
231      */
232     set : function(name, value){
233         if(this.data[name] == value){
234             return;
235         }
236         this.dirty = true;
237         if(!this.modified){
238             this.modified = {};
239         }
240         if(typeof this.modified[name] == 'undefined'){
241             this.modified[name] = this.data[name];
242         }
243         this.data[name] = value;
244         if(!this.editing && this.store){
245             this.store.afterEdit(this);
246         }       
247     },
248
249     /**
250      * Get the value of the named field.
251      * @param {String} name The name of the field to get the value of.
252      * @return {Object} The value of the field.
253      */
254     get : function(name){
255         return this.data[name]; 
256     },
257
258     // private
259     beginEdit : function(){
260         this.editing = true;
261         this.modified = {}; 
262     },
263
264     // private
265     cancelEdit : function(){
266         this.editing = false;
267         delete this.modified;
268     },
269
270     // private
271     endEdit : function(){
272         this.editing = false;
273         if(this.dirty && this.store){
274             this.store.afterEdit(this);
275         }
276     },
277
278     /**
279      * Usually called by the {@link Roo.data.Store} which owns the Record.
280      * Rejects all changes made to the Record since either creation, or the last commit operation.
281      * Modified fields are reverted to their original values.
282      * <p>
283      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
284      * of reject operations.
285      */
286     reject : function(){
287         var m = this.modified;
288         for(var n in m){
289             if(typeof m[n] != "function"){
290                 this.data[n] = m[n];
291             }
292         }
293         this.dirty = false;
294         delete this.modified;
295         this.editing = false;
296         if(this.store){
297             this.store.afterReject(this);
298         }
299     },
300
301     /**
302      * Usually called by the {@link Roo.data.Store} which owns the Record.
303      * Commits all changes made to the Record since either creation, or the last commit operation.
304      * <p>
305      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
306      * of commit operations.
307      */
308     commit : function(){
309         this.dirty = false;
310         delete this.modified;
311         this.editing = false;
312         if(this.store){
313             this.store.afterCommit(this);
314         }
315     },
316
317     // private
318     hasError : function(){
319         return this.error != null;
320     },
321
322     // private
323     clearError : function(){
324         this.error = null;
325     },
326
327     /**
328      * Creates a copy of this record.
329      * @param {String} id (optional) A new record id if you don't want to use this record's id
330      * @return {Record}
331      */
332     copy : function(newId) {
333         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
334     }
335 };/*
336  * Based on:
337  * Ext JS Library 1.1.1
338  * Copyright(c) 2006-2007, Ext JS, LLC.
339  *
340  * Originally Released Under LGPL - original licence link has changed is not relivant.
341  *
342  * Fork - LGPL
343  * <script type="text/javascript">
344  */
345
346
347
348 /**
349  * @class Roo.data.Store
350  * @extends Roo.util.Observable
351  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
352  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
353  * <p>
354  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
355  * has no knowledge of the format of the data returned by the Proxy.<br>
356  * <p>
357  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
358  * instances from the data object. These records are cached and made available through accessor functions.
359  * @constructor
360  * Creates a new Store.
361  * @param {Object} config A config object containing the objects needed for the Store to access data,
362  * and read the data into Records.
363  */
364 Roo.data.Store = function(config){
365     this.data = new Roo.util.MixedCollection(false);
366     this.data.getKey = function(o){
367         return o.id;
368     };
369     this.baseParams = {};
370     // private
371     this.paramNames = {
372         "start" : "start",
373         "limit" : "limit",
374         "sort" : "sort",
375         "dir" : "dir",
376         "multisort" : "_multisort"
377     };
378
379     if(config && config.data){
380         this.inlineData = config.data;
381         delete config.data;
382     }
383
384     Roo.apply(this, config);
385     
386     if(this.reader){ // reader passed
387         this.reader = Roo.factory(this.reader, Roo.data);
388         this.reader.xmodule = this.xmodule || false;
389         if(!this.recordType){
390             this.recordType = this.reader.recordType;
391         }
392         if(this.reader.onMetaChange){
393             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
394         }
395     }
396
397     if(this.recordType){
398         this.fields = this.recordType.prototype.fields;
399     }
400     this.modified = [];
401
402     this.addEvents({
403         /**
404          * @event datachanged
405          * Fires when the data cache has changed, and a widget which is using this Store
406          * as a Record cache should refresh its view.
407          * @param {Store} this
408          */
409         datachanged : true,
410         /**
411          * @event metachange
412          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
413          * @param {Store} this
414          * @param {Object} meta The JSON metadata
415          */
416         metachange : true,
417         /**
418          * @event add
419          * Fires when Records have been added to the Store
420          * @param {Store} this
421          * @param {Roo.data.Record[]} records The array of Records added
422          * @param {Number} index The index at which the record(s) were added
423          */
424         add : true,
425         /**
426          * @event remove
427          * Fires when a Record has been removed from the Store
428          * @param {Store} this
429          * @param {Roo.data.Record} record The Record that was removed
430          * @param {Number} index The index at which the record was removed
431          */
432         remove : true,
433         /**
434          * @event update
435          * Fires when a Record has been updated
436          * @param {Store} this
437          * @param {Roo.data.Record} record The Record that was updated
438          * @param {String} operation The update operation being performed.  Value may be one of:
439          * <pre><code>
440  Roo.data.Record.EDIT
441  Roo.data.Record.REJECT
442  Roo.data.Record.COMMIT
443          * </code></pre>
444          */
445         update : true,
446         /**
447          * @event clear
448          * Fires when the data cache has been cleared.
449          * @param {Store} this
450          */
451         clear : true,
452         /**
453          * @event beforeload
454          * Fires before a request is made for a new data object.  If the beforeload handler returns false
455          * the load action will be canceled.
456          * @param {Store} this
457          * @param {Object} options The loading options that were specified (see {@link #load} for details)
458          */
459         beforeload : true,
460         /**
461          * @event beforeloadadd
462          * Fires after a new set of Records has been loaded.
463          * @param {Store} this
464          * @param {Roo.data.Record[]} records The Records that were loaded
465          * @param {Object} options The loading options that were specified (see {@link #load} for details)
466          */
467         beforeloadadd : true,
468         /**
469          * @event load
470          * Fires after a new set of Records has been loaded, before they are added to the store.
471          * @param {Store} this
472          * @param {Roo.data.Record[]} records The Records that were loaded
473          * @param {Object} options The loading options that were specified (see {@link #load} for details)
474          * @params {Object} return from reader
475          */
476         load : true,
477         /**
478          * @event loadexception
479          * Fires if an exception occurs in the Proxy during loading.
480          * Called with the signature of the Proxy's "loadexception" event.
481          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
482          * 
483          * @param {Proxy} 
484          * @param {Object} return from JsonData.reader() - success, totalRecords, records
485          * @param {Object} load options 
486          * @param {Object} jsonData from your request (normally this contains the Exception)
487          */
488         loadexception : true
489     });
490     
491     if(this.proxy){
492         this.proxy = Roo.factory(this.proxy, Roo.data);
493         this.proxy.xmodule = this.xmodule || false;
494         this.relayEvents(this.proxy,  ["loadexception"]);
495     }
496     this.sortToggle = {};
497     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
498
499     Roo.data.Store.superclass.constructor.call(this);
500
501     if(this.inlineData){
502         this.loadData(this.inlineData);
503         delete this.inlineData;
504     }
505 };
506
507 Roo.extend(Roo.data.Store, Roo.util.Observable, {
508      /**
509     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
510     * without a remote query - used by combo/forms at present.
511     */
512     
513     /**
514     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
515     */
516     /**
517     * @cfg {Array} data Inline data to be loaded when the store is initialized.
518     */
519     /**
520     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
521     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
522     */
523     /**
524     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
525     * on any HTTP request
526     */
527     /**
528     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
529     */
530     /**
531     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
532     */
533     multiSort: false,
534     /**
535     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
536     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
537     */
538     remoteSort : false,
539
540     /**
541     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
542      * loaded or when a record is removed. (defaults to false).
543     */
544     pruneModifiedRecords : false,
545
546     // private
547     lastOptions : null,
548
549     /**
550      * Add Records to the Store and fires the add event.
551      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
552      */
553     add : function(records){
554         records = [].concat(records);
555         for(var i = 0, len = records.length; i < len; i++){
556             records[i].join(this);
557         }
558         var index = this.data.length;
559         this.data.addAll(records);
560         this.fireEvent("add", this, records, index);
561     },
562
563     /**
564      * Remove a Record from the Store and fires the remove event.
565      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
566      */
567     remove : function(record){
568         var index = this.data.indexOf(record);
569         this.data.removeAt(index);
570  
571         if(this.pruneModifiedRecords){
572             this.modified.remove(record);
573         }
574         this.fireEvent("remove", this, record, index);
575     },
576
577     /**
578      * Remove all Records from the Store and fires the clear event.
579      */
580     removeAll : function(){
581         this.data.clear();
582         if(this.pruneModifiedRecords){
583             this.modified = [];
584         }
585         this.fireEvent("clear", this);
586     },
587
588     /**
589      * Inserts Records to the Store at the given index and fires the add event.
590      * @param {Number} index The start index at which to insert the passed Records.
591      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
592      */
593     insert : function(index, records){
594         records = [].concat(records);
595         for(var i = 0, len = records.length; i < len; i++){
596             this.data.insert(index, records[i]);
597             records[i].join(this);
598         }
599         this.fireEvent("add", this, records, index);
600     },
601
602     /**
603      * Get the index within the cache of the passed Record.
604      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
605      * @return {Number} The index of the passed Record. Returns -1 if not found.
606      */
607     indexOf : function(record){
608         return this.data.indexOf(record);
609     },
610
611     /**
612      * Get the index within the cache of the Record with the passed id.
613      * @param {String} id The id of the Record to find.
614      * @return {Number} The index of the Record. Returns -1 if not found.
615      */
616     indexOfId : function(id){
617         return this.data.indexOfKey(id);
618     },
619
620     /**
621      * Get the Record with the specified id.
622      * @param {String} id The id of the Record to find.
623      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
624      */
625     getById : function(id){
626         return this.data.key(id);
627     },
628
629     /**
630      * Get the Record at the specified index.
631      * @param {Number} index The index of the Record to find.
632      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
633      */
634     getAt : function(index){
635         return this.data.itemAt(index);
636     },
637
638     /**
639      * Returns a range of Records between specified indices.
640      * @param {Number} startIndex (optional) The starting index (defaults to 0)
641      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
642      * @return {Roo.data.Record[]} An array of Records
643      */
644     getRange : function(start, end){
645         return this.data.getRange(start, end);
646     },
647
648     // private
649     storeOptions : function(o){
650         o = Roo.apply({}, o);
651         delete o.callback;
652         delete o.scope;
653         this.lastOptions = o;
654     },
655
656     /**
657      * Loads the Record cache from the configured Proxy using the configured Reader.
658      * <p>
659      * If using remote paging, then the first load call must specify the <em>start</em>
660      * and <em>limit</em> properties in the options.params property to establish the initial
661      * position within the dataset, and the number of Records to cache on each read from the Proxy.
662      * <p>
663      * <strong>It is important to note that for remote data sources, loading is asynchronous,
664      * and this call will return before the new data has been loaded. Perform any post-processing
665      * in a callback function, or in a "load" event handler.</strong>
666      * <p>
667      * @param {Object} options An object containing properties which control loading options:<ul>
668      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
669      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
670      * passed the following arguments:<ul>
671      * <li>r : Roo.data.Record[]</li>
672      * <li>options: Options object from the load call</li>
673      * <li>success: Boolean success indicator</li></ul></li>
674      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
675      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
676      * </ul>
677      */
678     load : function(options){
679         options = options || {};
680         if(this.fireEvent("beforeload", this, options) !== false){
681             this.storeOptions(options);
682             var p = Roo.apply(options.params || {}, this.baseParams);
683             // if meta was not loaded from remote source.. try requesting it.
684             if (!this.reader.metaFromRemote) {
685                 p._requestMeta = 1;
686             }
687             if(this.sortInfo && this.remoteSort){
688                 var pn = this.paramNames;
689                 p[pn["sort"]] = this.sortInfo.field;
690                 p[pn["dir"]] = this.sortInfo.direction;
691             }
692             if (this.multiSort) {
693                 var pn = this.paramNames;
694                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
695             }
696             
697             this.proxy.load(p, this.reader, this.loadRecords, this, options);
698         }
699     },
700
701     /**
702      * Reloads the Record cache from the configured Proxy using the configured Reader and
703      * the options from the last load operation performed.
704      * @param {Object} options (optional) An object containing properties which may override the options
705      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
706      * the most recently used options are reused).
707      */
708     reload : function(options){
709         this.load(Roo.applyIf(options||{}, this.lastOptions));
710     },
711
712     // private
713     // Called as a callback by the Reader during a load operation.
714     loadRecords : function(o, options, success){
715         if(!o || success === false){
716             if(success !== false){
717                 this.fireEvent("load", this, [], options, o);
718             }
719             if(options.callback){
720                 options.callback.call(options.scope || this, [], options, false);
721             }
722             return;
723         }
724         // if data returned failure - throw an exception.
725         if (o.success === false) {
726             // show a message if no listener is registered.
727             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
728                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
729             }
730             // loadmask wil be hooked into this..
731             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
732             return;
733         }
734         var r = o.records, t = o.totalRecords || r.length;
735         
736         this.fireEvent("beforeloadadd", this, r, options, o);
737         
738         if(!options || options.add !== true){
739             if(this.pruneModifiedRecords){
740                 this.modified = [];
741             }
742             for(var i = 0, len = r.length; i < len; i++){
743                 r[i].join(this);
744             }
745             if(this.snapshot){
746                 this.data = this.snapshot;
747                 delete this.snapshot;
748             }
749             this.data.clear();
750             this.data.addAll(r);
751             this.totalLength = t;
752             this.applySort();
753             this.fireEvent("datachanged", this);
754         }else{
755             this.totalLength = Math.max(t, this.data.length+r.length);
756             this.add(r);
757         }
758         
759         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
760                 
761             var e = new Roo.data.Record({});
762
763             e.set(this.parent.displayField, this.parent.emptyTitle);
764             e.set(this.parent.valueField, '');
765
766             this.insert(0, e);
767         }
768             
769         this.fireEvent("load", this, r, options, o);
770         if(options.callback){
771             options.callback.call(options.scope || this, r, options, true);
772         }
773     },
774
775
776     /**
777      * Loads data from a passed data block. A Reader which understands the format of the data
778      * must have been configured in the constructor.
779      * @param {Object} data The data block from which to read the Records.  The format of the data expected
780      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
781      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
782      */
783     loadData : function(o, append){
784         var r = this.reader.readRecords(o);
785         this.loadRecords(r, {add: append}, true);
786     },
787
788     /**
789      * Gets the number of cached records.
790      * <p>
791      * <em>If using paging, this may not be the total size of the dataset. If the data object
792      * used by the Reader contains the dataset size, then the getTotalCount() function returns
793      * the data set size</em>
794      */
795     getCount : function(){
796         return this.data.length || 0;
797     },
798
799     /**
800      * Gets the total number of records in the dataset as returned by the server.
801      * <p>
802      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
803      * the dataset size</em>
804      */
805     getTotalCount : function(){
806         return this.totalLength || 0;
807     },
808
809     /**
810      * Returns the sort state of the Store as an object with two properties:
811      * <pre><code>
812  field {String} The name of the field by which the Records are sorted
813  direction {String} The sort order, "ASC" or "DESC"
814      * </code></pre>
815      */
816     getSortState : function(){
817         return this.sortInfo;
818     },
819
820     // private
821     applySort : function(){
822         if(this.sortInfo && !this.remoteSort){
823             var s = this.sortInfo, f = s.field;
824             var st = this.fields.get(f).sortType;
825             var fn = function(r1, r2){
826                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
827                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
828             };
829             this.data.sort(s.direction, fn);
830             if(this.snapshot && this.snapshot != this.data){
831                 this.snapshot.sort(s.direction, fn);
832             }
833         }
834     },
835
836     /**
837      * Sets the default sort column and order to be used by the next load operation.
838      * @param {String} fieldName The name of the field to sort by.
839      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
840      */
841     setDefaultSort : function(field, dir){
842         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
843     },
844
845     /**
846      * Sort the Records.
847      * If remote sorting is used, the sort is performed on the server, and the cache is
848      * reloaded. If local sorting is used, the cache is sorted internally.
849      * @param {String} fieldName The name of the field to sort by.
850      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
851      */
852     sort : function(fieldName, dir){
853         var f = this.fields.get(fieldName);
854         if(!dir){
855             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
856             
857             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
858                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
859             }else{
860                 dir = f.sortDir;
861             }
862         }
863         this.sortToggle[f.name] = dir;
864         this.sortInfo = {field: f.name, direction: dir};
865         if(!this.remoteSort){
866             this.applySort();
867             this.fireEvent("datachanged", this);
868         }else{
869             this.load(this.lastOptions);
870         }
871     },
872
873     /**
874      * Calls the specified function for each of the Records in the cache.
875      * @param {Function} fn The function to call. The Record is passed as the first parameter.
876      * Returning <em>false</em> aborts and exits the iteration.
877      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
878      */
879     each : function(fn, scope){
880         this.data.each(fn, scope);
881     },
882
883     /**
884      * Gets all records modified since the last commit.  Modified records are persisted across load operations
885      * (e.g., during paging).
886      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
887      */
888     getModifiedRecords : function(){
889         return this.modified;
890     },
891
892     // private
893     createFilterFn : function(property, value, anyMatch){
894         if(!value.exec){ // not a regex
895             value = String(value);
896             if(value.length == 0){
897                 return false;
898             }
899             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
900         }
901         return function(r){
902             return value.test(r.data[property]);
903         };
904     },
905
906     /**
907      * Sums the value of <i>property</i> for each record between start and end and returns the result.
908      * @param {String} property A field on your records
909      * @param {Number} start The record index to start at (defaults to 0)
910      * @param {Number} end The last record index to include (defaults to length - 1)
911      * @return {Number} The sum
912      */
913     sum : function(property, start, end){
914         var rs = this.data.items, v = 0;
915         start = start || 0;
916         end = (end || end === 0) ? end : rs.length-1;
917
918         for(var i = start; i <= end; i++){
919             v += (rs[i].data[property] || 0);
920         }
921         return v;
922     },
923
924     /**
925      * Filter the records by a specified property.
926      * @param {String} field A field on your records
927      * @param {String/RegExp} value Either a string that the field
928      * should start with or a RegExp to test against the field
929      * @param {Boolean} anyMatch True to match any part not just the beginning
930      */
931     filter : function(property, value, anyMatch){
932         var fn = this.createFilterFn(property, value, anyMatch);
933         return fn ? this.filterBy(fn) : this.clearFilter();
934     },
935
936     /**
937      * Filter by a function. The specified function will be called with each
938      * record in this data source. If the function returns true the record is included,
939      * otherwise it is filtered.
940      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
941      * @param {Object} scope (optional) The scope of the function (defaults to this)
942      */
943     filterBy : function(fn, scope){
944         this.snapshot = this.snapshot || this.data;
945         this.data = this.queryBy(fn, scope||this);
946         this.fireEvent("datachanged", this);
947     },
948
949     /**
950      * Query the records by a specified property.
951      * @param {String} field A field on your records
952      * @param {String/RegExp} value Either a string that the field
953      * should start with or a RegExp to test against the field
954      * @param {Boolean} anyMatch True to match any part not just the beginning
955      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
956      */
957     query : function(property, value, anyMatch){
958         var fn = this.createFilterFn(property, value, anyMatch);
959         return fn ? this.queryBy(fn) : this.data.clone();
960     },
961
962     /**
963      * Query by a function. The specified function will be called with each
964      * record in this data source. If the function returns true the record is included
965      * in the results.
966      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
967      * @param {Object} scope (optional) The scope of the function (defaults to this)
968       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
969      **/
970     queryBy : function(fn, scope){
971         var data = this.snapshot || this.data;
972         return data.filterBy(fn, scope||this);
973     },
974
975     /**
976      * Collects unique values for a particular dataIndex from this store.
977      * @param {String} dataIndex The property to collect
978      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
979      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
980      * @return {Array} An array of the unique values
981      **/
982     collect : function(dataIndex, allowNull, bypassFilter){
983         var d = (bypassFilter === true && this.snapshot) ?
984                 this.snapshot.items : this.data.items;
985         var v, sv, r = [], l = {};
986         for(var i = 0, len = d.length; i < len; i++){
987             v = d[i].data[dataIndex];
988             sv = String(v);
989             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
990                 l[sv] = true;
991                 r[r.length] = v;
992             }
993         }
994         return r;
995     },
996
997     /**
998      * Revert to a view of the Record cache with no filtering applied.
999      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
1000      */
1001     clearFilter : function(suppressEvent){
1002         if(this.snapshot && this.snapshot != this.data){
1003             this.data = this.snapshot;
1004             delete this.snapshot;
1005             if(suppressEvent !== true){
1006                 this.fireEvent("datachanged", this);
1007             }
1008         }
1009     },
1010
1011     // private
1012     afterEdit : function(record){
1013         if(this.modified.indexOf(record) == -1){
1014             this.modified.push(record);
1015         }
1016         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
1017     },
1018     
1019     // private
1020     afterReject : function(record){
1021         this.modified.remove(record);
1022         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
1023     },
1024
1025     // private
1026     afterCommit : function(record){
1027         this.modified.remove(record);
1028         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
1029     },
1030
1031     /**
1032      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
1033      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
1034      */
1035     commitChanges : function(){
1036         var m = this.modified.slice(0);
1037         this.modified = [];
1038         for(var i = 0, len = m.length; i < len; i++){
1039             m[i].commit();
1040         }
1041     },
1042
1043     /**
1044      * Cancel outstanding changes on all changed records.
1045      */
1046     rejectChanges : function(){
1047         var m = this.modified.slice(0);
1048         this.modified = [];
1049         for(var i = 0, len = m.length; i < len; i++){
1050             m[i].reject();
1051         }
1052     },
1053
1054     onMetaChange : function(meta, rtype, o){
1055         this.recordType = rtype;
1056         this.fields = rtype.prototype.fields;
1057         delete this.snapshot;
1058         this.sortInfo = meta.sortInfo || this.sortInfo;
1059         this.modified = [];
1060         this.fireEvent('metachange', this, this.reader.meta);
1061     },
1062     
1063     moveIndex : function(data, type)
1064     {
1065         var index = this.indexOf(data);
1066         
1067         var newIndex = index + type;
1068         
1069         this.remove(data);
1070         
1071         this.insert(newIndex, data);
1072         
1073     }
1074 });/*
1075  * Based on:
1076  * Ext JS Library 1.1.1
1077  * Copyright(c) 2006-2007, Ext JS, LLC.
1078  *
1079  * Originally Released Under LGPL - original licence link has changed is not relivant.
1080  *
1081  * Fork - LGPL
1082  * <script type="text/javascript">
1083  */
1084
1085 /**
1086  * @class Roo.data.SimpleStore
1087  * @extends Roo.data.Store
1088  * Small helper class to make creating Stores from Array data easier.
1089  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
1090  * @cfg {Array} fields An array of field definition objects, or field name strings.
1091  * @cfg {Array} data The multi-dimensional array of data
1092  * @constructor
1093  * @param {Object} config
1094  */
1095 Roo.data.SimpleStore = function(config){
1096     Roo.data.SimpleStore.superclass.constructor.call(this, {
1097         isLocal : true,
1098         reader: new Roo.data.ArrayReader({
1099                 id: config.id
1100             },
1101             Roo.data.Record.create(config.fields)
1102         ),
1103         proxy : new Roo.data.MemoryProxy(config.data)
1104     });
1105     this.load();
1106 };
1107 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
1108  * Based on:
1109  * Ext JS Library 1.1.1
1110  * Copyright(c) 2006-2007, Ext JS, LLC.
1111  *
1112  * Originally Released Under LGPL - original licence link has changed is not relivant.
1113  *
1114  * Fork - LGPL
1115  * <script type="text/javascript">
1116  */
1117
1118 /**
1119 /**
1120  * @extends Roo.data.Store
1121  * @class Roo.data.JsonStore
1122  * Small helper class to make creating Stores for JSON data easier. <br/>
1123 <pre><code>
1124 var store = new Roo.data.JsonStore({
1125     url: 'get-images.php',
1126     root: 'images',
1127     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
1128 });
1129 </code></pre>
1130  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
1131  * JsonReader and HttpProxy (unless inline data is provided).</b>
1132  * @cfg {Array} fields An array of field definition objects, or field name strings.
1133  * @constructor
1134  * @param {Object} config
1135  */
1136 Roo.data.JsonStore = function(c){
1137     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
1138         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
1139         reader: new Roo.data.JsonReader(c, c.fields)
1140     }));
1141 };
1142 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
1143  * Based on:
1144  * Ext JS Library 1.1.1
1145  * Copyright(c) 2006-2007, Ext JS, LLC.
1146  *
1147  * Originally Released Under LGPL - original licence link has changed is not relivant.
1148  *
1149  * Fork - LGPL
1150  * <script type="text/javascript">
1151  */
1152
1153  
1154 Roo.data.Field = function(config){
1155     if(typeof config == "string"){
1156         config = {name: config};
1157     }
1158     Roo.apply(this, config);
1159     
1160     if(!this.type){
1161         this.type = "auto";
1162     }
1163     
1164     var st = Roo.data.SortTypes;
1165     // named sortTypes are supported, here we look them up
1166     if(typeof this.sortType == "string"){
1167         this.sortType = st[this.sortType];
1168     }
1169     
1170     // set default sortType for strings and dates
1171     if(!this.sortType){
1172         switch(this.type){
1173             case "string":
1174                 this.sortType = st.asUCString;
1175                 break;
1176             case "date":
1177                 this.sortType = st.asDate;
1178                 break;
1179             default:
1180                 this.sortType = st.none;
1181         }
1182     }
1183
1184     // define once
1185     var stripRe = /[\$,%]/g;
1186
1187     // prebuilt conversion function for this field, instead of
1188     // switching every time we're reading a value
1189     if(!this.convert){
1190         var cv, dateFormat = this.dateFormat;
1191         switch(this.type){
1192             case "":
1193             case "auto":
1194             case undefined:
1195                 cv = function(v){ return v; };
1196                 break;
1197             case "string":
1198                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
1199                 break;
1200             case "int":
1201                 cv = function(v){
1202                     return v !== undefined && v !== null && v !== '' ?
1203                            parseInt(String(v).replace(stripRe, ""), 10) : '';
1204                     };
1205                 break;
1206             case "float":
1207                 cv = function(v){
1208                     return v !== undefined && v !== null && v !== '' ?
1209                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
1210                     };
1211                 break;
1212             case "bool":
1213             case "boolean":
1214                 cv = function(v){ return v === true || v === "true" || v == 1; };
1215                 break;
1216             case "date":
1217                 cv = function(v){
1218                     if(!v){
1219                         return '';
1220                     }
1221                     if(v instanceof Date){
1222                         return v;
1223                     }
1224                     if(dateFormat){
1225                         if(dateFormat == "timestamp"){
1226                             return new Date(v*1000);
1227                         }
1228                         return Date.parseDate(v, dateFormat);
1229                     }
1230                     var parsed = Date.parse(v);
1231                     return parsed ? new Date(parsed) : null;
1232                 };
1233              break;
1234             
1235         }
1236         this.convert = cv;
1237     }
1238 };
1239
1240 Roo.data.Field.prototype = {
1241     dateFormat: null,
1242     defaultValue: "",
1243     mapping: null,
1244     sortType : null,
1245     sortDir : "ASC"
1246 };/*
1247  * Based on:
1248  * Ext JS Library 1.1.1
1249  * Copyright(c) 2006-2007, Ext JS, LLC.
1250  *
1251  * Originally Released Under LGPL - original licence link has changed is not relivant.
1252  *
1253  * Fork - LGPL
1254  * <script type="text/javascript">
1255  */
1256  
1257 // Base class for reading structured data from a data source.  This class is intended to be
1258 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
1259
1260 /**
1261  * @class Roo.data.DataReader
1262  * Base class for reading structured data from a data source.  This class is intended to be
1263  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
1264  */
1265
1266 Roo.data.DataReader = function(meta, recordType){
1267     
1268     this.meta = meta;
1269     
1270     this.recordType = recordType instanceof Array ? 
1271         Roo.data.Record.create(recordType) : recordType;
1272 };
1273
1274 Roo.data.DataReader.prototype = {
1275      /**
1276      * Create an empty record
1277      * @param {Object} data (optional) - overlay some values
1278      * @return {Roo.data.Record} record created.
1279      */
1280     newRow :  function(d) {
1281         var da =  {};
1282         this.recordType.prototype.fields.each(function(c) {
1283             switch( c.type) {
1284                 case 'int' : da[c.name] = 0; break;
1285                 case 'date' : da[c.name] = new Date(); break;
1286                 case 'float' : da[c.name] = 0.0; break;
1287                 case 'boolean' : da[c.name] = false; break;
1288                 default : da[c.name] = ""; break;
1289             }
1290             
1291         });
1292         return new this.recordType(Roo.apply(da, d));
1293     }
1294     
1295 };/*
1296  * Based on:
1297  * Ext JS Library 1.1.1
1298  * Copyright(c) 2006-2007, Ext JS, LLC.
1299  *
1300  * Originally Released Under LGPL - original licence link has changed is not relivant.
1301  *
1302  * Fork - LGPL
1303  * <script type="text/javascript">
1304  */
1305
1306 /**
1307  * @class Roo.data.DataProxy
1308  * @extends Roo.data.Observable
1309  * This class is an abstract base class for implementations which provide retrieval of
1310  * unformatted data objects.<br>
1311  * <p>
1312  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
1313  * (of the appropriate type which knows how to parse the data object) to provide a block of
1314  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
1315  * <p>
1316  * Custom implementations must implement the load method as described in
1317  * {@link Roo.data.HttpProxy#load}.
1318  */
1319 Roo.data.DataProxy = function(){
1320     this.addEvents({
1321         /**
1322          * @event beforeload
1323          * Fires before a network request is made to retrieve a data object.
1324          * @param {Object} This DataProxy object.
1325          * @param {Object} params The params parameter to the load function.
1326          */
1327         beforeload : true,
1328         /**
1329          * @event load
1330          * Fires before the load method's callback is called.
1331          * @param {Object} This DataProxy object.
1332          * @param {Object} o The data object.
1333          * @param {Object} arg The callback argument object passed to the load function.
1334          */
1335         load : true,
1336         /**
1337          * @event loadexception
1338          * Fires if an Exception occurs during data retrieval.
1339          * @param {Object} This DataProxy object.
1340          * @param {Object} o The data object.
1341          * @param {Object} arg The callback argument object passed to the load function.
1342          * @param {Object} e The Exception.
1343          */
1344         loadexception : true
1345     });
1346     Roo.data.DataProxy.superclass.constructor.call(this);
1347 };
1348
1349 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
1350
1351     /**
1352      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
1353      */
1354 /*
1355  * Based on:
1356  * Ext JS Library 1.1.1
1357  * Copyright(c) 2006-2007, Ext JS, LLC.
1358  *
1359  * Originally Released Under LGPL - original licence link has changed is not relivant.
1360  *
1361  * Fork - LGPL
1362  * <script type="text/javascript">
1363  */
1364 /**
1365  * @class Roo.data.MemoryProxy
1366  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
1367  * to the Reader when its load method is called.
1368  * @constructor
1369  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
1370  */
1371 Roo.data.MemoryProxy = function(data){
1372     if (data.data) {
1373         data = data.data;
1374     }
1375     Roo.data.MemoryProxy.superclass.constructor.call(this);
1376     this.data = data;
1377 };
1378
1379 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
1380     
1381     /**
1382      * Load data from the requested source (in this case an in-memory
1383      * data object passed to the constructor), read the data object into
1384      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1385      * process that block using the passed callback.
1386      * @param {Object} params This parameter is not used by the MemoryProxy class.
1387      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1388      * object into a block of Roo.data.Records.
1389      * @param {Function} callback The function into which to pass the block of Roo.data.records.
1390      * The function must be passed <ul>
1391      * <li>The Record block object</li>
1392      * <li>The "arg" argument from the load function</li>
1393      * <li>A boolean success indicator</li>
1394      * </ul>
1395      * @param {Object} scope The scope in which to call the callback
1396      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1397      */
1398     load : function(params, reader, callback, scope, arg){
1399         params = params || {};
1400         var result;
1401         try {
1402             result = reader.readRecords(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  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
2142  * @constructor
2143  * Create a new JsonReader
2144  * @param {Object} meta Metadata configuration options.
2145  * @param {Object} recordType Either an Array of field definition objects
2146  * as specified to {@link Roo.data.Record#create},
2147  * or an {@link Roo.data.Record} object
2148  * created using {@link Roo.data.Record#create}.
2149  */
2150 Roo.data.ArrayReader = function(meta, recordType){
2151     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
2152 };
2153
2154 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
2155     /**
2156      * Create a data block containing Roo.data.Records from an XML document.
2157      * @param {Object} o An Array of row objects which represents the dataset.
2158      * @return {Object} data A data block which is used by an Roo.data.Store object as
2159      * a cache of Roo.data.Records.
2160      */
2161     readRecords : function(o){
2162         var sid = this.meta ? this.meta.id : null;
2163         var recordType = this.recordType, fields = recordType.prototype.fields;
2164         var records = [];
2165         var root = o;
2166             for(var i = 0; i < root.length; i++){
2167                     var n = root[i];
2168                 var values = {};
2169                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
2170                 for(var j = 0, jlen = fields.length; j < jlen; j++){
2171                 var f = fields.items[j];
2172                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
2173                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
2174                 v = f.convert(v);
2175                 values[f.name] = v;
2176             }
2177                 var record = new recordType(values, id);
2178                 record.json = n;
2179                 records[records.length] = record;
2180             }
2181             return {
2182                 records : records,
2183                 totalRecords : records.length
2184             };
2185     }
2186 });/*
2187  * Based on:
2188  * Ext JS Library 1.1.1
2189  * Copyright(c) 2006-2007, Ext JS, LLC.
2190  *
2191  * Originally Released Under LGPL - original licence link has changed is not relivant.
2192  *
2193  * Fork - LGPL
2194  * <script type="text/javascript">
2195  */
2196
2197
2198 /**
2199  * @class Roo.data.Tree
2200  * @extends Roo.util.Observable
2201  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
2202  * in the tree have most standard DOM functionality.
2203  * @constructor
2204  * @param {Node} root (optional) The root node
2205  */
2206 Roo.data.Tree = function(root){
2207    this.nodeHash = {};
2208    /**
2209     * The root node for this tree
2210     * @type Node
2211     */
2212    this.root = null;
2213    if(root){
2214        this.setRootNode(root);
2215    }
2216    this.addEvents({
2217        /**
2218         * @event append
2219         * Fires when a new child node is appended to a node in this tree.
2220         * @param {Tree} tree The owner tree
2221         * @param {Node} parent The parent node
2222         * @param {Node} node The newly appended node
2223         * @param {Number} index The index of the newly appended node
2224         */
2225        "append" : true,
2226        /**
2227         * @event remove
2228         * Fires when a child node is removed from a node in this tree.
2229         * @param {Tree} tree The owner tree
2230         * @param {Node} parent The parent node
2231         * @param {Node} node The child node removed
2232         */
2233        "remove" : true,
2234        /**
2235         * @event move
2236         * Fires when a node is moved to a new location in the tree
2237         * @param {Tree} tree The owner tree
2238         * @param {Node} node The node moved
2239         * @param {Node} oldParent The old parent of this node
2240         * @param {Node} newParent The new parent of this node
2241         * @param {Number} index The index it was moved to
2242         */
2243        "move" : true,
2244        /**
2245         * @event insert
2246         * Fires when a new child node is inserted in a node in this tree.
2247         * @param {Tree} tree The owner tree
2248         * @param {Node} parent The parent node
2249         * @param {Node} node The child node inserted
2250         * @param {Node} refNode The child node the node was inserted before
2251         */
2252        "insert" : true,
2253        /**
2254         * @event beforeappend
2255         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
2256         * @param {Tree} tree The owner tree
2257         * @param {Node} parent The parent node
2258         * @param {Node} node The child node to be appended
2259         */
2260        "beforeappend" : true,
2261        /**
2262         * @event beforeremove
2263         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
2264         * @param {Tree} tree The owner tree
2265         * @param {Node} parent The parent node
2266         * @param {Node} node The child node to be removed
2267         */
2268        "beforeremove" : true,
2269        /**
2270         * @event beforemove
2271         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
2272         * @param {Tree} tree The owner tree
2273         * @param {Node} node The node being moved
2274         * @param {Node} oldParent The parent of the node
2275         * @param {Node} newParent The new parent the node is moving to
2276         * @param {Number} index The index it is being moved to
2277         */
2278        "beforemove" : true,
2279        /**
2280         * @event beforeinsert
2281         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
2282         * @param {Tree} tree The owner tree
2283         * @param {Node} parent The parent node
2284         * @param {Node} node The child node to be inserted
2285         * @param {Node} refNode The child node the node is being inserted before
2286         */
2287        "beforeinsert" : true
2288    });
2289
2290     Roo.data.Tree.superclass.constructor.call(this);
2291 };
2292
2293 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
2294     pathSeparator: "/",
2295
2296     proxyNodeEvent : function(){
2297         return this.fireEvent.apply(this, arguments);
2298     },
2299
2300     /**
2301      * Returns the root node for this tree.
2302      * @return {Node}
2303      */
2304     getRootNode : function(){
2305         return this.root;
2306     },
2307
2308     /**
2309      * Sets the root node for this tree.
2310      * @param {Node} node
2311      * @return {Node}
2312      */
2313     setRootNode : function(node){
2314         this.root = node;
2315         node.ownerTree = this;
2316         node.isRoot = true;
2317         this.registerNode(node);
2318         return node;
2319     },
2320
2321     /**
2322      * Gets a node in this tree by its id.
2323      * @param {String} id
2324      * @return {Node}
2325      */
2326     getNodeById : function(id){
2327         return this.nodeHash[id];
2328     },
2329
2330     registerNode : function(node){
2331         this.nodeHash[node.id] = node;
2332     },
2333
2334     unregisterNode : function(node){
2335         delete this.nodeHash[node.id];
2336     },
2337
2338     toString : function(){
2339         return "[Tree"+(this.id?" "+this.id:"")+"]";
2340     }
2341 });
2342
2343 /**
2344  * @class Roo.data.Node
2345  * @extends Roo.util.Observable
2346  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
2347  * @cfg {String} id The id for this node. If one is not specified, one is generated.
2348  * @constructor
2349  * @param {Object} attributes The attributes/config for the node
2350  */
2351 Roo.data.Node = function(attributes){
2352     /**
2353      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
2354      * @type {Object}
2355      */
2356     this.attributes = attributes || {};
2357     this.leaf = this.attributes.leaf;
2358     /**
2359      * The node id. @type String
2360      */
2361     this.id = this.attributes.id;
2362     if(!this.id){
2363         this.id = Roo.id(null, "ynode-");
2364         this.attributes.id = this.id;
2365     }
2366      
2367     
2368     /**
2369      * All child nodes of this node. @type Array
2370      */
2371     this.childNodes = [];
2372     if(!this.childNodes.indexOf){ // indexOf is a must
2373         this.childNodes.indexOf = function(o){
2374             for(var i = 0, len = this.length; i < len; i++){
2375                 if(this[i] == o) {
2376                     return i;
2377                 }
2378             }
2379             return -1;
2380         };
2381     }
2382     /**
2383      * The parent node for this node. @type Node
2384      */
2385     this.parentNode = null;
2386     /**
2387      * The first direct child node of this node, or null if this node has no child nodes. @type Node
2388      */
2389     this.firstChild = null;
2390     /**
2391      * The last direct child node of this node, or null if this node has no child nodes. @type Node
2392      */
2393     this.lastChild = null;
2394     /**
2395      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
2396      */
2397     this.previousSibling = null;
2398     /**
2399      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
2400      */
2401     this.nextSibling = null;
2402
2403     this.addEvents({
2404        /**
2405         * @event append
2406         * Fires when a new child node is appended
2407         * @param {Tree} tree The owner tree
2408         * @param {Node} this This node
2409         * @param {Node} node The newly appended node
2410         * @param {Number} index The index of the newly appended node
2411         */
2412        "append" : true,
2413        /**
2414         * @event remove
2415         * Fires when a child node is removed
2416         * @param {Tree} tree The owner tree
2417         * @param {Node} this This node
2418         * @param {Node} node The removed node
2419         */
2420        "remove" : true,
2421        /**
2422         * @event move
2423         * Fires when this node is moved to a new location in the tree
2424         * @param {Tree} tree The owner tree
2425         * @param {Node} this This node
2426         * @param {Node} oldParent The old parent of this node
2427         * @param {Node} newParent The new parent of this node
2428         * @param {Number} index The index it was moved to
2429         */
2430        "move" : true,
2431        /**
2432         * @event insert
2433         * Fires when a new child node is inserted.
2434         * @param {Tree} tree The owner tree
2435         * @param {Node} this This node
2436         * @param {Node} node The child node inserted
2437         * @param {Node} refNode The child node the node was inserted before
2438         */
2439        "insert" : true,
2440        /**
2441         * @event beforeappend
2442         * Fires before a new child is appended, return false to cancel the append.
2443         * @param {Tree} tree The owner tree
2444         * @param {Node} this This node
2445         * @param {Node} node The child node to be appended
2446         */
2447        "beforeappend" : true,
2448        /**
2449         * @event beforeremove
2450         * Fires before a child is removed, return false to cancel the remove.
2451         * @param {Tree} tree The owner tree
2452         * @param {Node} this This node
2453         * @param {Node} node The child node to be removed
2454         */
2455        "beforeremove" : true,
2456        /**
2457         * @event beforemove
2458         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
2459         * @param {Tree} tree The owner tree
2460         * @param {Node} this This node
2461         * @param {Node} oldParent The parent of this node
2462         * @param {Node} newParent The new parent this node is moving to
2463         * @param {Number} index The index it is being moved to
2464         */
2465        "beforemove" : true,
2466        /**
2467         * @event beforeinsert
2468         * Fires before a new child is inserted, return false to cancel the insert.
2469         * @param {Tree} tree The owner tree
2470         * @param {Node} this This node
2471         * @param {Node} node The child node to be inserted
2472         * @param {Node} refNode The child node the node is being inserted before
2473         */
2474        "beforeinsert" : true
2475    });
2476     this.listeners = this.attributes.listeners;
2477     Roo.data.Node.superclass.constructor.call(this);
2478 };
2479
2480 Roo.extend(Roo.data.Node, Roo.util.Observable, {
2481     fireEvent : function(evtName){
2482         // first do standard event for this node
2483         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
2484             return false;
2485         }
2486         // then bubble it up to the tree if the event wasn't cancelled
2487         var ot = this.getOwnerTree();
2488         if(ot){
2489             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
2490                 return false;
2491             }
2492         }
2493         return true;
2494     },
2495
2496     /**
2497      * Returns true if this node is a leaf
2498      * @return {Boolean}
2499      */
2500     isLeaf : function(){
2501         return this.leaf === true;
2502     },
2503
2504     // private
2505     setFirstChild : function(node){
2506         this.firstChild = node;
2507     },
2508
2509     //private
2510     setLastChild : function(node){
2511         this.lastChild = node;
2512     },
2513
2514
2515     /**
2516      * Returns true if this node is the last child of its parent
2517      * @return {Boolean}
2518      */
2519     isLast : function(){
2520        return (!this.parentNode ? true : this.parentNode.lastChild == this);
2521     },
2522
2523     /**
2524      * Returns true if this node is the first child of its parent
2525      * @return {Boolean}
2526      */
2527     isFirst : function(){
2528        return (!this.parentNode ? true : this.parentNode.firstChild == this);
2529     },
2530
2531     hasChildNodes : function(){
2532         return !this.isLeaf() && this.childNodes.length > 0;
2533     },
2534
2535     /**
2536      * Insert node(s) as the last child node of this node.
2537      * @param {Node/Array} node The node or Array of nodes to append
2538      * @return {Node} The appended node if single append, or null if an array was passed
2539      */
2540     appendChild : function(node){
2541         var multi = false;
2542         if(node instanceof Array){
2543             multi = node;
2544         }else if(arguments.length > 1){
2545             multi = arguments;
2546         }
2547         // if passed an array or multiple args do them one by one
2548         if(multi){
2549             for(var i = 0, len = multi.length; i < len; i++) {
2550                 this.appendChild(multi[i]);
2551             }
2552         }else{
2553             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
2554                 return false;
2555             }
2556             var index = this.childNodes.length;
2557             var oldParent = node.parentNode;
2558             // it's a move, make sure we move it cleanly
2559             if(oldParent){
2560                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
2561                     return false;
2562                 }
2563                 oldParent.removeChild(node);
2564             }
2565             index = this.childNodes.length;
2566             if(index == 0){
2567                 this.setFirstChild(node);
2568             }
2569             this.childNodes.push(node);
2570             node.parentNode = this;
2571             var ps = this.childNodes[index-1];
2572             if(ps){
2573                 node.previousSibling = ps;
2574                 ps.nextSibling = node;
2575             }else{
2576                 node.previousSibling = null;
2577             }
2578             node.nextSibling = null;
2579             this.setLastChild(node);
2580             node.setOwnerTree(this.getOwnerTree());
2581             this.fireEvent("append", this.ownerTree, this, node, index);
2582             if(oldParent){
2583                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
2584             }
2585             return node;
2586         }
2587     },
2588
2589     /**
2590      * Removes a child node from this node.
2591      * @param {Node} node The node to remove
2592      * @return {Node} The removed node
2593      */
2594     removeChild : function(node){
2595         var index = this.childNodes.indexOf(node);
2596         if(index == -1){
2597             return false;
2598         }
2599         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
2600             return false;
2601         }
2602
2603         // remove it from childNodes collection
2604         this.childNodes.splice(index, 1);
2605
2606         // update siblings
2607         if(node.previousSibling){
2608             node.previousSibling.nextSibling = node.nextSibling;
2609         }
2610         if(node.nextSibling){
2611             node.nextSibling.previousSibling = node.previousSibling;
2612         }
2613
2614         // update child refs
2615         if(this.firstChild == node){
2616             this.setFirstChild(node.nextSibling);
2617         }
2618         if(this.lastChild == node){
2619             this.setLastChild(node.previousSibling);
2620         }
2621
2622         node.setOwnerTree(null);
2623         // clear any references from the node
2624         node.parentNode = null;
2625         node.previousSibling = null;
2626         node.nextSibling = null;
2627         this.fireEvent("remove", this.ownerTree, this, node);
2628         return node;
2629     },
2630
2631     /**
2632      * Inserts the first node before the second node in this nodes childNodes collection.
2633      * @param {Node} node The node to insert
2634      * @param {Node} refNode The node to insert before (if null the node is appended)
2635      * @return {Node} The inserted node
2636      */
2637     insertBefore : function(node, refNode){
2638         if(!refNode){ // like standard Dom, refNode can be null for append
2639             return this.appendChild(node);
2640         }
2641         // nothing to do
2642         if(node == refNode){
2643             return false;
2644         }
2645
2646         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
2647             return false;
2648         }
2649         var index = this.childNodes.indexOf(refNode);
2650         var oldParent = node.parentNode;
2651         var refIndex = index;
2652
2653         // when moving internally, indexes will change after remove
2654         if(oldParent == this && this.childNodes.indexOf(node) < index){
2655             refIndex--;
2656         }
2657
2658         // it's a move, make sure we move it cleanly
2659         if(oldParent){
2660             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
2661                 return false;
2662             }
2663             oldParent.removeChild(node);
2664         }
2665         if(refIndex == 0){
2666             this.setFirstChild(node);
2667         }
2668         this.childNodes.splice(refIndex, 0, node);
2669         node.parentNode = this;
2670         var ps = this.childNodes[refIndex-1];
2671         if(ps){
2672             node.previousSibling = ps;
2673             ps.nextSibling = node;
2674         }else{
2675             node.previousSibling = null;
2676         }
2677         node.nextSibling = refNode;
2678         refNode.previousSibling = node;
2679         node.setOwnerTree(this.getOwnerTree());
2680         this.fireEvent("insert", this.ownerTree, this, node, refNode);
2681         if(oldParent){
2682             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
2683         }
2684         return node;
2685     },
2686
2687     /**
2688      * Returns the child node at the specified index.
2689      * @param {Number} index
2690      * @return {Node}
2691      */
2692     item : function(index){
2693         return this.childNodes[index];
2694     },
2695
2696     /**
2697      * Replaces one child node in this node with another.
2698      * @param {Node} newChild The replacement node
2699      * @param {Node} oldChild The node to replace
2700      * @return {Node} The replaced node
2701      */
2702     replaceChild : function(newChild, oldChild){
2703         this.insertBefore(newChild, oldChild);
2704         this.removeChild(oldChild);
2705         return oldChild;
2706     },
2707
2708     /**
2709      * Returns the index of a child node
2710      * @param {Node} node
2711      * @return {Number} The index of the node or -1 if it was not found
2712      */
2713     indexOf : function(child){
2714         return this.childNodes.indexOf(child);
2715     },
2716
2717     /**
2718      * Returns the tree this node is in.
2719      * @return {Tree}
2720      */
2721     getOwnerTree : function(){
2722         // if it doesn't have one, look for one
2723         if(!this.ownerTree){
2724             var p = this;
2725             while(p){
2726                 if(p.ownerTree){
2727                     this.ownerTree = p.ownerTree;
2728                     break;
2729                 }
2730                 p = p.parentNode;
2731             }
2732         }
2733         return this.ownerTree;
2734     },
2735
2736     /**
2737      * Returns depth of this node (the root node has a depth of 0)
2738      * @return {Number}
2739      */
2740     getDepth : function(){
2741         var depth = 0;
2742         var p = this;
2743         while(p.parentNode){
2744             ++depth;
2745             p = p.parentNode;
2746         }
2747         return depth;
2748     },
2749
2750     // private
2751     setOwnerTree : function(tree){
2752         // if it's move, we need to update everyone
2753         if(tree != this.ownerTree){
2754             if(this.ownerTree){
2755                 this.ownerTree.unregisterNode(this);
2756             }
2757             this.ownerTree = tree;
2758             var cs = this.childNodes;
2759             for(var i = 0, len = cs.length; i < len; i++) {
2760                 cs[i].setOwnerTree(tree);
2761             }
2762             if(tree){
2763                 tree.registerNode(this);
2764             }
2765         }
2766     },
2767
2768     /**
2769      * Returns the path for this node. The path can be used to expand or select this node programmatically.
2770      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
2771      * @return {String} The path
2772      */
2773     getPath : function(attr){
2774         attr = attr || "id";
2775         var p = this.parentNode;
2776         var b = [this.attributes[attr]];
2777         while(p){
2778             b.unshift(p.attributes[attr]);
2779             p = p.parentNode;
2780         }
2781         var sep = this.getOwnerTree().pathSeparator;
2782         return sep + b.join(sep);
2783     },
2784
2785     /**
2786      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2787      * function call will be the scope provided or the current node. The arguments to the function
2788      * will be the args provided or the current node. If the function returns false at any point,
2789      * the bubble is stopped.
2790      * @param {Function} fn The function to call
2791      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2792      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2793      */
2794     bubble : function(fn, scope, args){
2795         var p = this;
2796         while(p){
2797             if(fn.call(scope || p, args || p) === false){
2798                 break;
2799             }
2800             p = p.parentNode;
2801         }
2802     },
2803
2804     /**
2805      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2806      * function call will be the scope provided or the current node. The arguments to the function
2807      * will be the args provided or the current node. If the function returns false at any point,
2808      * the cascade is stopped on that branch.
2809      * @param {Function} fn The function to call
2810      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2811      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2812      */
2813     cascade : function(fn, scope, args){
2814         if(fn.call(scope || this, args || this) !== false){
2815             var cs = this.childNodes;
2816             for(var i = 0, len = cs.length; i < len; i++) {
2817                 cs[i].cascade(fn, scope, args);
2818             }
2819         }
2820     },
2821
2822     /**
2823      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
2824      * function call will be the scope provided or the current node. The arguments to the function
2825      * will be the args provided or the current node. If the function returns false at any point,
2826      * the iteration stops.
2827      * @param {Function} fn The function to call
2828      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2829      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2830      */
2831     eachChild : function(fn, scope, args){
2832         var cs = this.childNodes;
2833         for(var i = 0, len = cs.length; i < len; i++) {
2834                 if(fn.call(scope || this, args || cs[i]) === false){
2835                     break;
2836                 }
2837         }
2838     },
2839
2840     /**
2841      * Finds the first child that has the attribute with the specified value.
2842      * @param {String} attribute The attribute name
2843      * @param {Mixed} value The value to search for
2844      * @return {Node} The found child or null if none was found
2845      */
2846     findChild : function(attribute, value){
2847         var cs = this.childNodes;
2848         for(var i = 0, len = cs.length; i < len; i++) {
2849                 if(cs[i].attributes[attribute] == value){
2850                     return cs[i];
2851                 }
2852         }
2853         return null;
2854     },
2855
2856     /**
2857      * Finds the first child by a custom function. The child matches if the function passed
2858      * returns true.
2859      * @param {Function} fn
2860      * @param {Object} scope (optional)
2861      * @return {Node} The found child or null if none was found
2862      */
2863     findChildBy : function(fn, scope){
2864         var cs = this.childNodes;
2865         for(var i = 0, len = cs.length; i < len; i++) {
2866                 if(fn.call(scope||cs[i], cs[i]) === true){
2867                     return cs[i];
2868                 }
2869         }
2870         return null;
2871     },
2872
2873     /**
2874      * Sorts this nodes children using the supplied sort function
2875      * @param {Function} fn
2876      * @param {Object} scope (optional)
2877      */
2878     sort : function(fn, scope){
2879         var cs = this.childNodes;
2880         var len = cs.length;
2881         if(len > 0){
2882             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
2883             cs.sort(sortFn);
2884             for(var i = 0; i < len; i++){
2885                 var n = cs[i];
2886                 n.previousSibling = cs[i-1];
2887                 n.nextSibling = cs[i+1];
2888                 if(i == 0){
2889                     this.setFirstChild(n);
2890                 }
2891                 if(i == len-1){
2892                     this.setLastChild(n);
2893                 }
2894             }
2895         }
2896     },
2897
2898     /**
2899      * Returns true if this node is an ancestor (at any point) of the passed node.
2900      * @param {Node} node
2901      * @return {Boolean}
2902      */
2903     contains : function(node){
2904         return node.isAncestor(this);
2905     },
2906
2907     /**
2908      * Returns true if the passed node is an ancestor (at any point) of this node.
2909      * @param {Node} node
2910      * @return {Boolean}
2911      */
2912     isAncestor : function(node){
2913         var p = this.parentNode;
2914         while(p){
2915             if(p == node){
2916                 return true;
2917             }
2918             p = p.parentNode;
2919         }
2920         return false;
2921     },
2922
2923     toString : function(){
2924         return "[Node"+(this.id?" "+this.id:"")+"]";
2925     }
2926 });/*
2927  * Based on:
2928  * Ext JS Library 1.1.1
2929  * Copyright(c) 2006-2007, Ext JS, LLC.
2930  *
2931  * Originally Released Under LGPL - original licence link has changed is not relivant.
2932  *
2933  * Fork - LGPL
2934  * <script type="text/javascript">
2935  */
2936  (function(){ 
2937 /**
2938  * @class Roo.Layer
2939  * @extends Roo.Element
2940  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
2941  * automatic maintaining of shadow/shim positions.
2942  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
2943  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
2944  * you can pass a string with a CSS class name. False turns off the shadow.
2945  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
2946  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
2947  * @cfg {String} cls CSS class to add to the element
2948  * @cfg {Number} zindex Starting z-index (defaults to 11000)
2949  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
2950  * @constructor
2951  * @param {Object} config An object with config options.
2952  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
2953  */
2954
2955 Roo.Layer = function(config, existingEl){
2956     config = config || {};
2957     var dh = Roo.DomHelper;
2958     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
2959     if(existingEl){
2960         this.dom = Roo.getDom(existingEl);
2961     }
2962     if(!this.dom){
2963         var o = config.dh || {tag: "div", cls: "x-layer"};
2964         this.dom = dh.append(pel, o);
2965     }
2966     if(config.cls){
2967         this.addClass(config.cls);
2968     }
2969     this.constrain = config.constrain !== false;
2970     this.visibilityMode = Roo.Element.VISIBILITY;
2971     if(config.id){
2972         this.id = this.dom.id = config.id;
2973     }else{
2974         this.id = Roo.id(this.dom);
2975     }
2976     this.zindex = config.zindex || this.getZIndex();
2977     this.position("absolute", this.zindex);
2978     if(config.shadow){
2979         this.shadowOffset = config.shadowOffset || 4;
2980         this.shadow = new Roo.Shadow({
2981             offset : this.shadowOffset,
2982             mode : config.shadow
2983         });
2984     }else{
2985         this.shadowOffset = 0;
2986     }
2987     this.useShim = config.shim !== false && Roo.useShims;
2988     this.useDisplay = config.useDisplay;
2989     this.hide();
2990 };
2991
2992 var supr = Roo.Element.prototype;
2993
2994 // shims are shared among layer to keep from having 100 iframes
2995 var shims = [];
2996
2997 Roo.extend(Roo.Layer, Roo.Element, {
2998
2999     getZIndex : function(){
3000         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
3001     },
3002
3003     getShim : function(){
3004         if(!this.useShim){
3005             return null;
3006         }
3007         if(this.shim){
3008             return this.shim;
3009         }
3010         var shim = shims.shift();
3011         if(!shim){
3012             shim = this.createShim();
3013             shim.enableDisplayMode('block');
3014             shim.dom.style.display = 'none';
3015             shim.dom.style.visibility = 'visible';
3016         }
3017         var pn = this.dom.parentNode;
3018         if(shim.dom.parentNode != pn){
3019             pn.insertBefore(shim.dom, this.dom);
3020         }
3021         shim.setStyle('z-index', this.getZIndex()-2);
3022         this.shim = shim;
3023         return shim;
3024     },
3025
3026     hideShim : function(){
3027         if(this.shim){
3028             this.shim.setDisplayed(false);
3029             shims.push(this.shim);
3030             delete this.shim;
3031         }
3032     },
3033
3034     disableShadow : function(){
3035         if(this.shadow){
3036             this.shadowDisabled = true;
3037             this.shadow.hide();
3038             this.lastShadowOffset = this.shadowOffset;
3039             this.shadowOffset = 0;
3040         }
3041     },
3042
3043     enableShadow : function(show){
3044         if(this.shadow){
3045             this.shadowDisabled = false;
3046             this.shadowOffset = this.lastShadowOffset;
3047             delete this.lastShadowOffset;
3048             if(show){
3049                 this.sync(true);
3050             }
3051         }
3052     },
3053
3054     // private
3055     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
3056     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
3057     sync : function(doShow){
3058         var sw = this.shadow;
3059         if(!this.updating && this.isVisible() && (sw || this.useShim)){
3060             var sh = this.getShim();
3061
3062             var w = this.getWidth(),
3063                 h = this.getHeight();
3064
3065             var l = this.getLeft(true),
3066                 t = this.getTop(true);
3067
3068             if(sw && !this.shadowDisabled){
3069                 if(doShow && !sw.isVisible()){
3070                     sw.show(this);
3071                 }else{
3072                     sw.realign(l, t, w, h);
3073                 }
3074                 if(sh){
3075                     if(doShow){
3076                        sh.show();
3077                     }
3078                     // fit the shim behind the shadow, so it is shimmed too
3079                     var a = sw.adjusts, s = sh.dom.style;
3080                     s.left = (Math.min(l, l+a.l))+"px";
3081                     s.top = (Math.min(t, t+a.t))+"px";
3082                     s.width = (w+a.w)+"px";
3083                     s.height = (h+a.h)+"px";
3084                 }
3085             }else if(sh){
3086                 if(doShow){
3087                    sh.show();
3088                 }
3089                 sh.setSize(w, h);
3090                 sh.setLeftTop(l, t);
3091             }
3092             
3093         }
3094     },
3095
3096     // private
3097     destroy : function(){
3098         this.hideShim();
3099         if(this.shadow){
3100             this.shadow.hide();
3101         }
3102         this.removeAllListeners();
3103         var pn = this.dom.parentNode;
3104         if(pn){
3105             pn.removeChild(this.dom);
3106         }
3107         Roo.Element.uncache(this.id);
3108     },
3109
3110     remove : function(){
3111         this.destroy();
3112     },
3113
3114     // private
3115     beginUpdate : function(){
3116         this.updating = true;
3117     },
3118
3119     // private
3120     endUpdate : function(){
3121         this.updating = false;
3122         this.sync(true);
3123     },
3124
3125     // private
3126     hideUnders : function(negOffset){
3127         if(this.shadow){
3128             this.shadow.hide();
3129         }
3130         this.hideShim();
3131     },
3132
3133     // private
3134     constrainXY : function(){
3135         if(this.constrain){
3136             var vw = Roo.lib.Dom.getViewWidth(),
3137                 vh = Roo.lib.Dom.getViewHeight();
3138             var s = Roo.get(document).getScroll();
3139
3140             var xy = this.getXY();
3141             var x = xy[0], y = xy[1];   
3142             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
3143             // only move it if it needs it
3144             var moved = false;
3145             // first validate right/bottom
3146             if((x + w) > vw+s.left){
3147                 x = vw - w - this.shadowOffset;
3148                 moved = true;
3149             }
3150             if((y + h) > vh+s.top){
3151                 y = vh - h - this.shadowOffset;
3152                 moved = true;
3153             }
3154             // then make sure top/left isn't negative
3155             if(x < s.left){
3156                 x = s.left;
3157                 moved = true;
3158             }
3159             if(y < s.top){
3160                 y = s.top;
3161                 moved = true;
3162             }
3163             if(moved){
3164                 if(this.avoidY){
3165                     var ay = this.avoidY;
3166                     if(y <= ay && (y+h) >= ay){
3167                         y = ay-h-5;   
3168                     }
3169                 }
3170                 xy = [x, y];
3171                 this.storeXY(xy);
3172                 supr.setXY.call(this, xy);
3173                 this.sync();
3174             }
3175         }
3176     },
3177
3178     isVisible : function(){
3179         return this.visible;    
3180     },
3181
3182     // private
3183     showAction : function(){
3184         this.visible = true; // track visibility to prevent getStyle calls
3185         if(this.useDisplay === true){
3186             this.setDisplayed("");
3187         }else if(this.lastXY){
3188             supr.setXY.call(this, this.lastXY);
3189         }else if(this.lastLT){
3190             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
3191         }
3192     },
3193
3194     // private
3195     hideAction : function(){
3196         this.visible = false;
3197         if(this.useDisplay === true){
3198             this.setDisplayed(false);
3199         }else{
3200             this.setLeftTop(-10000,-10000);
3201         }
3202     },
3203
3204     // overridden Element method
3205     setVisible : function(v, a, d, c, e){
3206         if(v){
3207             this.showAction();
3208         }
3209         if(a && v){
3210             var cb = function(){
3211                 this.sync(true);
3212                 if(c){
3213                     c();
3214                 }
3215             }.createDelegate(this);
3216             supr.setVisible.call(this, true, true, d, cb, e);
3217         }else{
3218             if(!v){
3219                 this.hideUnders(true);
3220             }
3221             var cb = c;
3222             if(a){
3223                 cb = function(){
3224                     this.hideAction();
3225                     if(c){
3226                         c();
3227                     }
3228                 }.createDelegate(this);
3229             }
3230             supr.setVisible.call(this, v, a, d, cb, e);
3231             if(v){
3232                 this.sync(true);
3233             }else if(!a){
3234                 this.hideAction();
3235             }
3236         }
3237     },
3238
3239     storeXY : function(xy){
3240         delete this.lastLT;
3241         this.lastXY = xy;
3242     },
3243
3244     storeLeftTop : function(left, top){
3245         delete this.lastXY;
3246         this.lastLT = [left, top];
3247     },
3248
3249     // private
3250     beforeFx : function(){
3251         this.beforeAction();
3252         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
3253     },
3254
3255     // private
3256     afterFx : function(){
3257         Roo.Layer.superclass.afterFx.apply(this, arguments);
3258         this.sync(this.isVisible());
3259     },
3260
3261     // private
3262     beforeAction : function(){
3263         if(!this.updating && this.shadow){
3264             this.shadow.hide();
3265         }
3266     },
3267
3268     // overridden Element method
3269     setLeft : function(left){
3270         this.storeLeftTop(left, this.getTop(true));
3271         supr.setLeft.apply(this, arguments);
3272         this.sync();
3273     },
3274
3275     setTop : function(top){
3276         this.storeLeftTop(this.getLeft(true), top);
3277         supr.setTop.apply(this, arguments);
3278         this.sync();
3279     },
3280
3281     setLeftTop : function(left, top){
3282         this.storeLeftTop(left, top);
3283         supr.setLeftTop.apply(this, arguments);
3284         this.sync();
3285     },
3286
3287     setXY : function(xy, a, d, c, e){
3288         this.fixDisplay();
3289         this.beforeAction();
3290         this.storeXY(xy);
3291         var cb = this.createCB(c);
3292         supr.setXY.call(this, xy, a, d, cb, e);
3293         if(!a){
3294             cb();
3295         }
3296     },
3297
3298     // private
3299     createCB : function(c){
3300         var el = this;
3301         return function(){
3302             el.constrainXY();
3303             el.sync(true);
3304             if(c){
3305                 c();
3306             }
3307         };
3308     },
3309
3310     // overridden Element method
3311     setX : function(x, a, d, c, e){
3312         this.setXY([x, this.getY()], a, d, c, e);
3313     },
3314
3315     // overridden Element method
3316     setY : function(y, a, d, c, e){
3317         this.setXY([this.getX(), y], a, d, c, e);
3318     },
3319
3320     // overridden Element method
3321     setSize : function(w, h, a, d, c, e){
3322         this.beforeAction();
3323         var cb = this.createCB(c);
3324         supr.setSize.call(this, w, h, a, d, cb, e);
3325         if(!a){
3326             cb();
3327         }
3328     },
3329
3330     // overridden Element method
3331     setWidth : function(w, a, d, c, e){
3332         this.beforeAction();
3333         var cb = this.createCB(c);
3334         supr.setWidth.call(this, w, a, d, cb, e);
3335         if(!a){
3336             cb();
3337         }
3338     },
3339
3340     // overridden Element method
3341     setHeight : function(h, a, d, c, e){
3342         this.beforeAction();
3343         var cb = this.createCB(c);
3344         supr.setHeight.call(this, h, a, d, cb, e);
3345         if(!a){
3346             cb();
3347         }
3348     },
3349
3350     // overridden Element method
3351     setBounds : function(x, y, w, h, a, d, c, e){
3352         this.beforeAction();
3353         var cb = this.createCB(c);
3354         if(!a){
3355             this.storeXY([x, y]);
3356             supr.setXY.call(this, [x, y]);
3357             supr.setSize.call(this, w, h, a, d, cb, e);
3358             cb();
3359         }else{
3360             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
3361         }
3362         return this;
3363     },
3364     
3365     /**
3366      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
3367      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
3368      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
3369      * @param {Number} zindex The new z-index to set
3370      * @return {this} The Layer
3371      */
3372     setZIndex : function(zindex){
3373         this.zindex = zindex;
3374         this.setStyle("z-index", zindex + 2);
3375         if(this.shadow){
3376             this.shadow.setZIndex(zindex + 1);
3377         }
3378         if(this.shim){
3379             this.shim.setStyle("z-index", zindex);
3380         }
3381     }
3382 });
3383 })();/*
3384  * Based on:
3385  * Ext JS Library 1.1.1
3386  * Copyright(c) 2006-2007, Ext JS, LLC.
3387  *
3388  * Originally Released Under LGPL - original licence link has changed is not relivant.
3389  *
3390  * Fork - LGPL
3391  * <script type="text/javascript">
3392  */
3393
3394
3395 /**
3396  * @class Roo.Shadow
3397  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
3398  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
3399  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
3400  * @constructor
3401  * Create a new Shadow
3402  * @param {Object} config The config object
3403  */
3404 Roo.Shadow = function(config){
3405     Roo.apply(this, config);
3406     if(typeof this.mode != "string"){
3407         this.mode = this.defaultMode;
3408     }
3409     var o = this.offset, a = {h: 0};
3410     var rad = Math.floor(this.offset/2);
3411     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
3412         case "drop":
3413             a.w = 0;
3414             a.l = a.t = o;
3415             a.t -= 1;
3416             if(Roo.isIE){
3417                 a.l -= this.offset + rad;
3418                 a.t -= this.offset + rad;
3419                 a.w -= rad;
3420                 a.h -= rad;
3421                 a.t += 1;
3422             }
3423         break;
3424         case "sides":
3425             a.w = (o*2);
3426             a.l = -o;
3427             a.t = o-1;
3428             if(Roo.isIE){
3429                 a.l -= (this.offset - rad);
3430                 a.t -= this.offset + rad;
3431                 a.l += 1;
3432                 a.w -= (this.offset - rad)*2;
3433                 a.w -= rad + 1;
3434                 a.h -= 1;
3435             }
3436         break;
3437         case "frame":
3438             a.w = a.h = (o*2);
3439             a.l = a.t = -o;
3440             a.t += 1;
3441             a.h -= 2;
3442             if(Roo.isIE){
3443                 a.l -= (this.offset - rad);
3444                 a.t -= (this.offset - rad);
3445                 a.l += 1;
3446                 a.w -= (this.offset + rad + 1);
3447                 a.h -= (this.offset + rad);
3448                 a.h += 1;
3449             }
3450         break;
3451     };
3452
3453     this.adjusts = a;
3454 };
3455
3456 Roo.Shadow.prototype = {
3457     /**
3458      * @cfg {String} mode
3459      * The shadow display mode.  Supports the following options:<br />
3460      * sides: Shadow displays on both sides and bottom only<br />
3461      * frame: Shadow displays equally on all four sides<br />
3462      * drop: Traditional bottom-right drop shadow (default)
3463      */
3464     /**
3465      * @cfg {String} offset
3466      * The number of pixels to offset the shadow from the element (defaults to 4)
3467      */
3468     offset: 4,
3469
3470     // private
3471     defaultMode: "drop",
3472
3473     /**
3474      * Displays the shadow under the target element
3475      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
3476      */
3477     show : function(target){
3478         target = Roo.get(target);
3479         if(!this.el){
3480             this.el = Roo.Shadow.Pool.pull();
3481             if(this.el.dom.nextSibling != target.dom){
3482                 this.el.insertBefore(target);
3483             }
3484         }
3485         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
3486         if(Roo.isIE){
3487             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
3488         }
3489         this.realign(
3490             target.getLeft(true),
3491             target.getTop(true),
3492             target.getWidth(),
3493             target.getHeight()
3494         );
3495         this.el.dom.style.display = "block";
3496     },
3497
3498     /**
3499      * Returns true if the shadow is visible, else false
3500      */
3501     isVisible : function(){
3502         return this.el ? true : false;  
3503     },
3504
3505     /**
3506      * Direct alignment when values are already available. Show must be called at least once before
3507      * calling this method to ensure it is initialized.
3508      * @param {Number} left The target element left position
3509      * @param {Number} top The target element top position
3510      * @param {Number} width The target element width
3511      * @param {Number} height The target element height
3512      */
3513     realign : function(l, t, w, h){
3514         if(!this.el){
3515             return;
3516         }
3517         var a = this.adjusts, d = this.el.dom, s = d.style;
3518         var iea = 0;
3519         s.left = (l+a.l)+"px";
3520         s.top = (t+a.t)+"px";
3521         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
3522  
3523         if(s.width != sws || s.height != shs){
3524             s.width = sws;
3525             s.height = shs;
3526             if(!Roo.isIE){
3527                 var cn = d.childNodes;
3528                 var sww = Math.max(0, (sw-12))+"px";
3529                 cn[0].childNodes[1].style.width = sww;
3530                 cn[1].childNodes[1].style.width = sww;
3531                 cn[2].childNodes[1].style.width = sww;
3532                 cn[1].style.height = Math.max(0, (sh-12))+"px";
3533             }
3534         }
3535     },
3536
3537     /**
3538      * Hides this shadow
3539      */
3540     hide : function(){
3541         if(this.el){
3542             this.el.dom.style.display = "none";
3543             Roo.Shadow.Pool.push(this.el);
3544             delete this.el;
3545         }
3546     },
3547
3548     /**
3549      * Adjust the z-index of this shadow
3550      * @param {Number} zindex The new z-index
3551      */
3552     setZIndex : function(z){
3553         this.zIndex = z;
3554         if(this.el){
3555             this.el.setStyle("z-index", z);
3556         }
3557     }
3558 };
3559
3560 // Private utility class that manages the internal Shadow cache
3561 Roo.Shadow.Pool = function(){
3562     var p = [];
3563     var markup = Roo.isIE ?
3564                  '<div class="x-ie-shadow"></div>' :
3565                  '<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>';
3566     return {
3567         pull : function(){
3568             var sh = p.shift();
3569             if(!sh){
3570                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
3571                 sh.autoBoxAdjust = false;
3572             }
3573             return sh;
3574         },
3575
3576         push : function(sh){
3577             p.push(sh);
3578         }
3579     };
3580 }();/*
3581  * Based on:
3582  * Ext JS Library 1.1.1
3583  * Copyright(c) 2006-2007, Ext JS, LLC.
3584  *
3585  * Originally Released Under LGPL - original licence link has changed is not relivant.
3586  *
3587  * Fork - LGPL
3588  * <script type="text/javascript">
3589  */
3590
3591
3592 /**
3593  * @class Roo.SplitBar
3594  * @extends Roo.util.Observable
3595  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
3596  * <br><br>
3597  * Usage:
3598  * <pre><code>
3599 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
3600                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
3601 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
3602 split.minSize = 100;
3603 split.maxSize = 600;
3604 split.animate = true;
3605 split.on('moved', splitterMoved);
3606 </code></pre>
3607  * @constructor
3608  * Create a new SplitBar
3609  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
3610  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
3611  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3612  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
3613                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
3614                         position of the SplitBar).
3615  */
3616 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
3617     
3618     /** @private */
3619     this.el = Roo.get(dragElement, true);
3620     this.el.dom.unselectable = "on";
3621     /** @private */
3622     this.resizingEl = Roo.get(resizingElement, true);
3623
3624     /**
3625      * @private
3626      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3627      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
3628      * @type Number
3629      */
3630     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
3631     
3632     /**
3633      * The minimum size of the resizing element. (Defaults to 0)
3634      * @type Number
3635      */
3636     this.minSize = 0;
3637     
3638     /**
3639      * The maximum size of the resizing element. (Defaults to 2000)
3640      * @type Number
3641      */
3642     this.maxSize = 2000;
3643     
3644     /**
3645      * Whether to animate the transition to the new size
3646      * @type Boolean
3647      */
3648     this.animate = false;
3649     
3650     /**
3651      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
3652      * @type Boolean
3653      */
3654     this.useShim = false;
3655     
3656     /** @private */
3657     this.shim = null;
3658     
3659     if(!existingProxy){
3660         /** @private */
3661         this.proxy = Roo.SplitBar.createProxy(this.orientation);
3662     }else{
3663         this.proxy = Roo.get(existingProxy).dom;
3664     }
3665     /** @private */
3666     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
3667     
3668     /** @private */
3669     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
3670     
3671     /** @private */
3672     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
3673     
3674     /** @private */
3675     this.dragSpecs = {};
3676     
3677     /**
3678      * @private The adapter to use to positon and resize elements
3679      */
3680     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
3681     this.adapter.init(this);
3682     
3683     if(this.orientation == Roo.SplitBar.HORIZONTAL){
3684         /** @private */
3685         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
3686         this.el.addClass("x-splitbar-h");
3687     }else{
3688         /** @private */
3689         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
3690         this.el.addClass("x-splitbar-v");
3691     }
3692     
3693     this.addEvents({
3694         /**
3695          * @event resize
3696          * Fires when the splitter is moved (alias for {@link #event-moved})
3697          * @param {Roo.SplitBar} this
3698          * @param {Number} newSize the new width or height
3699          */
3700         "resize" : true,
3701         /**
3702          * @event moved
3703          * Fires when the splitter is moved
3704          * @param {Roo.SplitBar} this
3705          * @param {Number} newSize the new width or height
3706          */
3707         "moved" : true,
3708         /**
3709          * @event beforeresize
3710          * Fires before the splitter is dragged
3711          * @param {Roo.SplitBar} this
3712          */
3713         "beforeresize" : true,
3714
3715         "beforeapply" : true
3716     });
3717
3718     Roo.util.Observable.call(this);
3719 };
3720
3721 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
3722     onStartProxyDrag : function(x, y){
3723         this.fireEvent("beforeresize", this);
3724         if(!this.overlay){
3725             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
3726             o.unselectable();
3727             o.enableDisplayMode("block");
3728             // all splitbars share the same overlay
3729             Roo.SplitBar.prototype.overlay = o;
3730         }
3731         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
3732         this.overlay.show();
3733         Roo.get(this.proxy).setDisplayed("block");
3734         var size = this.adapter.getElementSize(this);
3735         this.activeMinSize = this.getMinimumSize();;
3736         this.activeMaxSize = this.getMaximumSize();;
3737         var c1 = size - this.activeMinSize;
3738         var c2 = Math.max(this.activeMaxSize - size, 0);
3739         if(this.orientation == Roo.SplitBar.HORIZONTAL){
3740             this.dd.resetConstraints();
3741             this.dd.setXConstraint(
3742                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
3743                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
3744             );
3745             this.dd.setYConstraint(0, 0);
3746         }else{
3747             this.dd.resetConstraints();
3748             this.dd.setXConstraint(0, 0);
3749             this.dd.setYConstraint(
3750                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
3751                 this.placement == Roo.SplitBar.TOP ? c2 : c1
3752             );
3753          }
3754         this.dragSpecs.startSize = size;
3755         this.dragSpecs.startPoint = [x, y];
3756         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
3757     },
3758     
3759     /** 
3760      * @private Called after the drag operation by the DDProxy
3761      */
3762     onEndProxyDrag : function(e){
3763         Roo.get(this.proxy).setDisplayed(false);
3764         var endPoint = Roo.lib.Event.getXY(e);
3765         if(this.overlay){
3766             this.overlay.hide();
3767         }
3768         var newSize;
3769         if(this.orientation == Roo.SplitBar.HORIZONTAL){
3770             newSize = this.dragSpecs.startSize + 
3771                 (this.placement == Roo.SplitBar.LEFT ?
3772                     endPoint[0] - this.dragSpecs.startPoint[0] :
3773                     this.dragSpecs.startPoint[0] - endPoint[0]
3774                 );
3775         }else{
3776             newSize = this.dragSpecs.startSize + 
3777                 (this.placement == Roo.SplitBar.TOP ?
3778                     endPoint[1] - this.dragSpecs.startPoint[1] :
3779                     this.dragSpecs.startPoint[1] - endPoint[1]
3780                 );
3781         }
3782         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
3783         if(newSize != this.dragSpecs.startSize){
3784             if(this.fireEvent('beforeapply', this, newSize) !== false){
3785                 this.adapter.setElementSize(this, newSize);
3786                 this.fireEvent("moved", this, newSize);
3787                 this.fireEvent("resize", this, newSize);
3788             }
3789         }
3790     },
3791     
3792     /**
3793      * Get the adapter this SplitBar uses
3794      * @return The adapter object
3795      */
3796     getAdapter : function(){
3797         return this.adapter;
3798     },
3799     
3800     /**
3801      * Set the adapter this SplitBar uses
3802      * @param {Object} adapter A SplitBar adapter object
3803      */
3804     setAdapter : function(adapter){
3805         this.adapter = adapter;
3806         this.adapter.init(this);
3807     },
3808     
3809     /**
3810      * Gets the minimum size for the resizing element
3811      * @return {Number} The minimum size
3812      */
3813     getMinimumSize : function(){
3814         return this.minSize;
3815     },
3816     
3817     /**
3818      * Sets the minimum size for the resizing element
3819      * @param {Number} minSize The minimum size
3820      */
3821     setMinimumSize : function(minSize){
3822         this.minSize = minSize;
3823     },
3824     
3825     /**
3826      * Gets the maximum size for the resizing element
3827      * @return {Number} The maximum size
3828      */
3829     getMaximumSize : function(){
3830         return this.maxSize;
3831     },
3832     
3833     /**
3834      * Sets the maximum size for the resizing element
3835      * @param {Number} maxSize The maximum size
3836      */
3837     setMaximumSize : function(maxSize){
3838         this.maxSize = maxSize;
3839     },
3840     
3841     /**
3842      * Sets the initialize size for the resizing element
3843      * @param {Number} size The initial size
3844      */
3845     setCurrentSize : function(size){
3846         var oldAnimate = this.animate;
3847         this.animate = false;
3848         this.adapter.setElementSize(this, size);
3849         this.animate = oldAnimate;
3850     },
3851     
3852     /**
3853      * Destroy this splitbar. 
3854      * @param {Boolean} removeEl True to remove the element
3855      */
3856     destroy : function(removeEl){
3857         if(this.shim){
3858             this.shim.remove();
3859         }
3860         this.dd.unreg();
3861         this.proxy.parentNode.removeChild(this.proxy);
3862         if(removeEl){
3863             this.el.remove();
3864         }
3865     }
3866 });
3867
3868 /**
3869  * @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.
3870  */
3871 Roo.SplitBar.createProxy = function(dir){
3872     var proxy = new Roo.Element(document.createElement("div"));
3873     proxy.unselectable();
3874     var cls = 'x-splitbar-proxy';
3875     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
3876     document.body.appendChild(proxy.dom);
3877     return proxy.dom;
3878 };
3879
3880 /** 
3881  * @class Roo.SplitBar.BasicLayoutAdapter
3882  * Default Adapter. It assumes the splitter and resizing element are not positioned
3883  * elements and only gets/sets the width of the element. Generally used for table based layouts.
3884  */
3885 Roo.SplitBar.BasicLayoutAdapter = function(){
3886 };
3887
3888 Roo.SplitBar.BasicLayoutAdapter.prototype = {
3889     // do nothing for now
3890     init : function(s){
3891     
3892     },
3893     /**
3894      * Called before drag operations to get the current size of the resizing element. 
3895      * @param {Roo.SplitBar} s The SplitBar using this adapter
3896      */
3897      getElementSize : function(s){
3898         if(s.orientation == Roo.SplitBar.HORIZONTAL){
3899             return s.resizingEl.getWidth();
3900         }else{
3901             return s.resizingEl.getHeight();
3902         }
3903     },
3904     
3905     /**
3906      * Called after drag operations to set the size of the resizing element.
3907      * @param {Roo.SplitBar} s The SplitBar using this adapter
3908      * @param {Number} newSize The new size to set
3909      * @param {Function} onComplete A function to be invoked when resizing is complete
3910      */
3911     setElementSize : function(s, newSize, onComplete){
3912         if(s.orientation == Roo.SplitBar.HORIZONTAL){
3913             if(!s.animate){
3914                 s.resizingEl.setWidth(newSize);
3915                 if(onComplete){
3916                     onComplete(s, newSize);
3917                 }
3918             }else{
3919                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
3920             }
3921         }else{
3922             
3923             if(!s.animate){
3924                 s.resizingEl.setHeight(newSize);
3925                 if(onComplete){
3926                     onComplete(s, newSize);
3927                 }
3928             }else{
3929                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
3930             }
3931         }
3932     }
3933 };
3934
3935 /** 
3936  *@class Roo.SplitBar.AbsoluteLayoutAdapter
3937  * @extends Roo.SplitBar.BasicLayoutAdapter
3938  * Adapter that  moves the splitter element to align with the resized sizing element. 
3939  * Used with an absolute positioned SplitBar.
3940  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
3941  * document.body, make sure you assign an id to the body element.
3942  */
3943 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
3944     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
3945     this.container = Roo.get(container);
3946 };
3947
3948 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
3949     init : function(s){
3950         this.basic.init(s);
3951     },
3952     
3953     getElementSize : function(s){
3954         return this.basic.getElementSize(s);
3955     },
3956     
3957     setElementSize : function(s, newSize, onComplete){
3958         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
3959     },
3960     
3961     moveSplitter : function(s){
3962         var yes = Roo.SplitBar;
3963         switch(s.placement){
3964             case yes.LEFT:
3965                 s.el.setX(s.resizingEl.getRight());
3966                 break;
3967             case yes.RIGHT:
3968                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
3969                 break;
3970             case yes.TOP:
3971                 s.el.setY(s.resizingEl.getBottom());
3972                 break;
3973             case yes.BOTTOM:
3974                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
3975                 break;
3976         }
3977     }
3978 };
3979
3980 /**
3981  * Orientation constant - Create a vertical SplitBar
3982  * @static
3983  * @type Number
3984  */
3985 Roo.SplitBar.VERTICAL = 1;
3986
3987 /**
3988  * Orientation constant - Create a horizontal SplitBar
3989  * @static
3990  * @type Number
3991  */
3992 Roo.SplitBar.HORIZONTAL = 2;
3993
3994 /**
3995  * Placement constant - The resizing element is to the left of the splitter element
3996  * @static
3997  * @type Number
3998  */
3999 Roo.SplitBar.LEFT = 1;
4000
4001 /**
4002  * Placement constant - The resizing element is to the right of the splitter element
4003  * @static
4004  * @type Number
4005  */
4006 Roo.SplitBar.RIGHT = 2;
4007
4008 /**
4009  * Placement constant - The resizing element is positioned above the splitter element
4010  * @static
4011  * @type Number
4012  */
4013 Roo.SplitBar.TOP = 3;
4014
4015 /**
4016  * Placement constant - The resizing element is positioned under splitter element
4017  * @static
4018  * @type Number
4019  */
4020 Roo.SplitBar.BOTTOM = 4;
4021 /*
4022  * Based on:
4023  * Ext JS Library 1.1.1
4024  * Copyright(c) 2006-2007, Ext JS, LLC.
4025  *
4026  * Originally Released Under LGPL - original licence link has changed is not relivant.
4027  *
4028  * Fork - LGPL
4029  * <script type="text/javascript">
4030  */
4031
4032 /**
4033  * @class Roo.View
4034  * @extends Roo.util.Observable
4035  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
4036  * This class also supports single and multi selection modes. <br>
4037  * Create a data model bound view:
4038  <pre><code>
4039  var store = new Roo.data.Store(...);
4040
4041  var view = new Roo.View({
4042     el : "my-element",
4043     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
4044  
4045     singleSelect: true,
4046     selectedClass: "ydataview-selected",
4047     store: store
4048  });
4049
4050  // listen for node click?
4051  view.on("click", function(vw, index, node, e){
4052  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
4053  });
4054
4055  // load XML data
4056  dataModel.load("foobar.xml");
4057  </code></pre>
4058  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
4059  * <br><br>
4060  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
4061  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
4062  * 
4063  * Note: old style constructor is still suported (container, template, config)
4064  * 
4065  * @constructor
4066  * Create a new View
4067  * @param {Object} config The config object
4068  * 
4069  */
4070 Roo.View = function(config, depreciated_tpl, depreciated_config){
4071     
4072     this.parent = false;
4073     
4074     if (typeof(depreciated_tpl) == 'undefined') {
4075         // new way.. - universal constructor.
4076         Roo.apply(this, config);
4077         this.el  = Roo.get(this.el);
4078     } else {
4079         // old format..
4080         this.el  = Roo.get(config);
4081         this.tpl = depreciated_tpl;
4082         Roo.apply(this, depreciated_config);
4083     }
4084     this.wrapEl  = this.el.wrap().wrap();
4085     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
4086     
4087     
4088     if(typeof(this.tpl) == "string"){
4089         this.tpl = new Roo.Template(this.tpl);
4090     } else {
4091         // support xtype ctors..
4092         this.tpl = new Roo.factory(this.tpl, Roo);
4093     }
4094     
4095     
4096     this.tpl.compile();
4097     
4098     /** @private */
4099     this.addEvents({
4100         /**
4101          * @event beforeclick
4102          * Fires before a click is processed. Returns false to cancel the default action.
4103          * @param {Roo.View} this
4104          * @param {Number} index The index of the target node
4105          * @param {HTMLElement} node The target node
4106          * @param {Roo.EventObject} e The raw event object
4107          */
4108             "beforeclick" : true,
4109         /**
4110          * @event click
4111          * Fires when a template node is clicked.
4112          * @param {Roo.View} this
4113          * @param {Number} index The index of the target node
4114          * @param {HTMLElement} node The target node
4115          * @param {Roo.EventObject} e The raw event object
4116          */
4117             "click" : true,
4118         /**
4119          * @event dblclick
4120          * Fires when a template node is double clicked.
4121          * @param {Roo.View} this
4122          * @param {Number} index The index of the target node
4123          * @param {HTMLElement} node The target node
4124          * @param {Roo.EventObject} e The raw event object
4125          */
4126             "dblclick" : true,
4127         /**
4128          * @event contextmenu
4129          * Fires when a template node is right clicked.
4130          * @param {Roo.View} this
4131          * @param {Number} index The index of the target node
4132          * @param {HTMLElement} node The target node
4133          * @param {Roo.EventObject} e The raw event object
4134          */
4135             "contextmenu" : true,
4136         /**
4137          * @event selectionchange
4138          * Fires when the selected nodes change.
4139          * @param {Roo.View} this
4140          * @param {Array} selections Array of the selected nodes
4141          */
4142             "selectionchange" : true,
4143     
4144         /**
4145          * @event beforeselect
4146          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
4147          * @param {Roo.View} this
4148          * @param {HTMLElement} node The node to be selected
4149          * @param {Array} selections Array of currently selected nodes
4150          */
4151             "beforeselect" : true,
4152         /**
4153          * @event preparedata
4154          * Fires on every row to render, to allow you to change the data.
4155          * @param {Roo.View} this
4156          * @param {Object} data to be rendered (change this)
4157          */
4158           "preparedata" : true
4159           
4160           
4161         });
4162
4163
4164
4165     this.el.on({
4166         "click": this.onClick,
4167         "dblclick": this.onDblClick,
4168         "contextmenu": this.onContextMenu,
4169         scope:this
4170     });
4171
4172     this.selections = [];
4173     this.nodes = [];
4174     this.cmp = new Roo.CompositeElementLite([]);
4175     if(this.store){
4176         this.store = Roo.factory(this.store, Roo.data);
4177         this.setStore(this.store, true);
4178     }
4179     
4180     if ( this.footer && this.footer.xtype) {
4181            
4182          var fctr = this.wrapEl.appendChild(document.createElement("div"));
4183         
4184         this.footer.dataSource = this.store;
4185         this.footer.container = fctr;
4186         this.footer = Roo.factory(this.footer, Roo);
4187         fctr.insertFirst(this.el);
4188         
4189         // this is a bit insane - as the paging toolbar seems to detach the el..
4190 //        dom.parentNode.parentNode.parentNode
4191          // they get detached?
4192     }
4193     
4194     
4195     Roo.View.superclass.constructor.call(this);
4196     
4197     
4198 };
4199
4200 Roo.extend(Roo.View, Roo.util.Observable, {
4201     
4202      /**
4203      * @cfg {Roo.data.Store} store Data store to load data from.
4204      */
4205     store : false,
4206     
4207     /**
4208      * @cfg {String|Roo.Element} el The container element.
4209      */
4210     el : '',
4211     
4212     /**
4213      * @cfg {String|Roo.Template} tpl The template used by this View 
4214      */
4215     tpl : false,
4216     /**
4217      * @cfg {String} dataName the named area of the template to use as the data area
4218      *                          Works with domtemplates roo-name="name"
4219      */
4220     dataName: false,
4221     /**
4222      * @cfg {String} selectedClass The css class to add to selected nodes
4223      */
4224     selectedClass : "x-view-selected",
4225      /**
4226      * @cfg {String} emptyText The empty text to show when nothing is loaded.
4227      */
4228     emptyText : "",
4229     
4230     /**
4231      * @cfg {String} text to display on mask (default Loading)
4232      */
4233     mask : false,
4234     /**
4235      * @cfg {Boolean} multiSelect Allow multiple selection
4236      */
4237     multiSelect : false,
4238     /**
4239      * @cfg {Boolean} singleSelect Allow single selection
4240      */
4241     singleSelect:  false,
4242     
4243     /**
4244      * @cfg {Boolean} toggleSelect - selecting 
4245      */
4246     toggleSelect : false,
4247     
4248     /**
4249      * @cfg {Boolean} tickable - selecting 
4250      */
4251     tickable : false,
4252     
4253     /**
4254      * Returns the element this view is bound to.
4255      * @return {Roo.Element}
4256      */
4257     getEl : function(){
4258         return this.wrapEl;
4259     },
4260     
4261     
4262
4263     /**
4264      * Refreshes the view. - called by datachanged on the store. - do not call directly.
4265      */
4266     refresh : function(){
4267         //Roo.log('refresh');
4268         var t = this.tpl;
4269         
4270         // if we are using something like 'domtemplate', then
4271         // the what gets used is:
4272         // t.applySubtemplate(NAME, data, wrapping data..)
4273         // the outer template then get' applied with
4274         //     the store 'extra data'
4275         // and the body get's added to the
4276         //      roo-name="data" node?
4277         //      <span class='roo-tpl-{name}'></span> ?????
4278         
4279         
4280         
4281         this.clearSelections();
4282         this.el.update("");
4283         var html = [];
4284         var records = this.store.getRange();
4285         if(records.length < 1) {
4286             
4287             // is this valid??  = should it render a template??
4288             
4289             this.el.update(this.emptyText);
4290             return;
4291         }
4292         var el = this.el;
4293         if (this.dataName) {
4294             this.el.update(t.apply(this.store.meta)); //????
4295             el = this.el.child('.roo-tpl-' + this.dataName);
4296         }
4297         
4298         for(var i = 0, len = records.length; i < len; i++){
4299             var data = this.prepareData(records[i].data, i, records[i]);
4300             this.fireEvent("preparedata", this, data, i, records[i]);
4301             
4302             var d = Roo.apply({}, data);
4303             
4304             if(this.tickable){
4305                 Roo.apply(d, {'roo-id' : Roo.id()});
4306                 
4307                 var _this = this;
4308             
4309                 Roo.each(this.parent.item, function(item){
4310                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
4311                         return;
4312                     }
4313                     Roo.apply(d, {'roo-data-checked' : 'checked'});
4314                 });
4315             }
4316             
4317             html[html.length] = Roo.util.Format.trim(
4318                 this.dataName ?
4319                     t.applySubtemplate(this.dataName, d, this.store.meta) :
4320                     t.apply(d)
4321             );
4322         }
4323         
4324         
4325         
4326         el.update(html.join(""));
4327         this.nodes = el.dom.childNodes;
4328         this.updateIndexes(0);
4329     },
4330     
4331
4332     /**
4333      * Function to override to reformat the data that is sent to
4334      * the template for each node.
4335      * DEPRICATED - use the preparedata event handler.
4336      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
4337      * a JSON object for an UpdateManager bound view).
4338      */
4339     prepareData : function(data, index, record)
4340     {
4341         this.fireEvent("preparedata", this, data, index, record);
4342         return data;
4343     },
4344
4345     onUpdate : function(ds, record){
4346         // Roo.log('on update');   
4347         this.clearSelections();
4348         var index = this.store.indexOf(record);
4349         var n = this.nodes[index];
4350         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
4351         n.parentNode.removeChild(n);
4352         this.updateIndexes(index, index);
4353     },
4354
4355     
4356     
4357 // --------- FIXME     
4358     onAdd : function(ds, records, index)
4359     {
4360         //Roo.log(['on Add', ds, records, index] );        
4361         this.clearSelections();
4362         if(this.nodes.length == 0){
4363             this.refresh();
4364             return;
4365         }
4366         var n = this.nodes[index];
4367         for(var i = 0, len = records.length; i < len; i++){
4368             var d = this.prepareData(records[i].data, i, records[i]);
4369             if(n){
4370                 this.tpl.insertBefore(n, d);
4371             }else{
4372                 
4373                 this.tpl.append(this.el, d);
4374             }
4375         }
4376         this.updateIndexes(index);
4377     },
4378
4379     onRemove : function(ds, record, index){
4380        // Roo.log('onRemove');
4381         this.clearSelections();
4382         var el = this.dataName  ?
4383             this.el.child('.roo-tpl-' + this.dataName) :
4384             this.el; 
4385         
4386         el.dom.removeChild(this.nodes[index]);
4387         this.updateIndexes(index);
4388     },
4389
4390     /**
4391      * Refresh an individual node.
4392      * @param {Number} index
4393      */
4394     refreshNode : function(index){
4395         this.onUpdate(this.store, this.store.getAt(index));
4396     },
4397
4398     updateIndexes : function(startIndex, endIndex){
4399         var ns = this.nodes;
4400         startIndex = startIndex || 0;
4401         endIndex = endIndex || ns.length - 1;
4402         for(var i = startIndex; i <= endIndex; i++){
4403             ns[i].nodeIndex = i;
4404         }
4405     },
4406
4407     /**
4408      * Changes the data store this view uses and refresh the view.
4409      * @param {Store} store
4410      */
4411     setStore : function(store, initial){
4412         if(!initial && this.store){
4413             this.store.un("datachanged", this.refresh);
4414             this.store.un("add", this.onAdd);
4415             this.store.un("remove", this.onRemove);
4416             this.store.un("update", this.onUpdate);
4417             this.store.un("clear", this.refresh);
4418             this.store.un("beforeload", this.onBeforeLoad);
4419             this.store.un("load", this.onLoad);
4420             this.store.un("loadexception", this.onLoad);
4421         }
4422         if(store){
4423           
4424             store.on("datachanged", this.refresh, this);
4425             store.on("add", this.onAdd, this);
4426             store.on("remove", this.onRemove, this);
4427             store.on("update", this.onUpdate, this);
4428             store.on("clear", this.refresh, this);
4429             store.on("beforeload", this.onBeforeLoad, this);
4430             store.on("load", this.onLoad, this);
4431             store.on("loadexception", this.onLoad, this);
4432         }
4433         
4434         if(store){
4435             this.refresh();
4436         }
4437     },
4438     /**
4439      * onbeforeLoad - masks the loading area.
4440      *
4441      */
4442     onBeforeLoad : function(store,opts)
4443     {
4444          //Roo.log('onBeforeLoad');   
4445         if (!opts.add) {
4446             this.el.update("");
4447         }
4448         this.el.mask(this.mask ? this.mask : "Loading" ); 
4449     },
4450     onLoad : function ()
4451     {
4452         this.el.unmask();
4453     },
4454     
4455
4456     /**
4457      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
4458      * @param {HTMLElement} node
4459      * @return {HTMLElement} The template node
4460      */
4461     findItemFromChild : function(node){
4462         var el = this.dataName  ?
4463             this.el.child('.roo-tpl-' + this.dataName,true) :
4464             this.el.dom; 
4465         
4466         if(!node || node.parentNode == el){
4467                     return node;
4468             }
4469             var p = node.parentNode;
4470             while(p && p != el){
4471             if(p.parentNode == el){
4472                 return p;
4473             }
4474             p = p.parentNode;
4475         }
4476             return null;
4477     },
4478
4479     /** @ignore */
4480     onClick : function(e){
4481         var item = this.findItemFromChild(e.getTarget());
4482         if(item){
4483             var index = this.indexOf(item);
4484             if(this.onItemClick(item, index, e) !== false){
4485                 this.fireEvent("click", this, index, item, e);
4486             }
4487         }else{
4488             this.clearSelections();
4489         }
4490     },
4491
4492     /** @ignore */
4493     onContextMenu : function(e){
4494         var item = this.findItemFromChild(e.getTarget());
4495         if(item){
4496             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
4497         }
4498     },
4499
4500     /** @ignore */
4501     onDblClick : function(e){
4502         var item = this.findItemFromChild(e.getTarget());
4503         if(item){
4504             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
4505         }
4506     },
4507
4508     onItemClick : function(item, index, e)
4509     {
4510         if(this.fireEvent("beforeclick", this, index, item, e) === false){
4511             return false;
4512         }
4513         if (this.toggleSelect) {
4514             var m = this.isSelected(item) ? 'unselect' : 'select';
4515             //Roo.log(m);
4516             var _t = this;
4517             _t[m](item, true, false);
4518             return true;
4519         }
4520         if(this.multiSelect || this.singleSelect){
4521             if(this.multiSelect && e.shiftKey && this.lastSelection){
4522                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
4523             }else{
4524                 this.select(item, this.multiSelect && e.ctrlKey);
4525                 this.lastSelection = item;
4526             }
4527             
4528             if(!this.tickable){
4529                 e.preventDefault();
4530             }
4531             
4532         }
4533         return true;
4534     },
4535
4536     /**
4537      * Get the number of selected nodes.
4538      * @return {Number}
4539      */
4540     getSelectionCount : function(){
4541         return this.selections.length;
4542     },
4543
4544     /**
4545      * Get the currently selected nodes.
4546      * @return {Array} An array of HTMLElements
4547      */
4548     getSelectedNodes : function(){
4549         return this.selections;
4550     },
4551
4552     /**
4553      * Get the indexes of the selected nodes.
4554      * @return {Array}
4555      */
4556     getSelectedIndexes : function(){
4557         var indexes = [], s = this.selections;
4558         for(var i = 0, len = s.length; i < len; i++){
4559             indexes.push(s[i].nodeIndex);
4560         }
4561         return indexes;
4562     },
4563
4564     /**
4565      * Clear all selections
4566      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
4567      */
4568     clearSelections : function(suppressEvent){
4569         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
4570             this.cmp.elements = this.selections;
4571             this.cmp.removeClass(this.selectedClass);
4572             this.selections = [];
4573             if(!suppressEvent){
4574                 this.fireEvent("selectionchange", this, this.selections);
4575             }
4576         }
4577     },
4578
4579     /**
4580      * Returns true if the passed node is selected
4581      * @param {HTMLElement/Number} node The node or node index
4582      * @return {Boolean}
4583      */
4584     isSelected : function(node){
4585         var s = this.selections;
4586         if(s.length < 1){
4587             return false;
4588         }
4589         node = this.getNode(node);
4590         return s.indexOf(node) !== -1;
4591     },
4592
4593     /**
4594      * Selects nodes.
4595      * @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
4596      * @param {Boolean} keepExisting (optional) true to keep existing selections
4597      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4598      */
4599     select : function(nodeInfo, keepExisting, suppressEvent){
4600         if(nodeInfo instanceof Array){
4601             if(!keepExisting){
4602                 this.clearSelections(true);
4603             }
4604             for(var i = 0, len = nodeInfo.length; i < len; i++){
4605                 this.select(nodeInfo[i], true, true);
4606             }
4607             return;
4608         } 
4609         var node = this.getNode(nodeInfo);
4610         if(!node || this.isSelected(node)){
4611             return; // already selected.
4612         }
4613         if(!keepExisting){
4614             this.clearSelections(true);
4615         }
4616         
4617         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
4618             Roo.fly(node).addClass(this.selectedClass);
4619             this.selections.push(node);
4620             if(!suppressEvent){
4621                 this.fireEvent("selectionchange", this, this.selections);
4622             }
4623         }
4624         
4625         
4626     },
4627       /**
4628      * Unselects nodes.
4629      * @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
4630      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
4631      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4632      */
4633     unselect : function(nodeInfo, keepExisting, suppressEvent)
4634     {
4635         if(nodeInfo instanceof Array){
4636             Roo.each(this.selections, function(s) {
4637                 this.unselect(s, nodeInfo);
4638             }, this);
4639             return;
4640         }
4641         var node = this.getNode(nodeInfo);
4642         if(!node || !this.isSelected(node)){
4643             //Roo.log("not selected");
4644             return; // not selected.
4645         }
4646         // fireevent???
4647         var ns = [];
4648         Roo.each(this.selections, function(s) {
4649             if (s == node ) {
4650                 Roo.fly(node).removeClass(this.selectedClass);
4651
4652                 return;
4653             }
4654             ns.push(s);
4655         },this);
4656         
4657         this.selections= ns;
4658         this.fireEvent("selectionchange", this, this.selections);
4659     },
4660
4661     /**
4662      * Gets a template node.
4663      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4664      * @return {HTMLElement} The node or null if it wasn't found
4665      */
4666     getNode : function(nodeInfo){
4667         if(typeof nodeInfo == "string"){
4668             return document.getElementById(nodeInfo);
4669         }else if(typeof nodeInfo == "number"){
4670             return this.nodes[nodeInfo];
4671         }
4672         return nodeInfo;
4673     },
4674
4675     /**
4676      * Gets a range template nodes.
4677      * @param {Number} startIndex
4678      * @param {Number} endIndex
4679      * @return {Array} An array of nodes
4680      */
4681     getNodes : function(start, end){
4682         var ns = this.nodes;
4683         start = start || 0;
4684         end = typeof end == "undefined" ? ns.length - 1 : end;
4685         var nodes = [];
4686         if(start <= end){
4687             for(var i = start; i <= end; i++){
4688                 nodes.push(ns[i]);
4689             }
4690         } else{
4691             for(var i = start; i >= end; i--){
4692                 nodes.push(ns[i]);
4693             }
4694         }
4695         return nodes;
4696     },
4697
4698     /**
4699      * Finds the index of the passed node
4700      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4701      * @return {Number} The index of the node or -1
4702      */
4703     indexOf : function(node){
4704         node = this.getNode(node);
4705         if(typeof node.nodeIndex == "number"){
4706             return node.nodeIndex;
4707         }
4708         var ns = this.nodes;
4709         for(var i = 0, len = ns.length; i < len; i++){
4710             if(ns[i] == node){
4711                 return i;
4712             }
4713         }
4714         return -1;
4715     }
4716 });
4717 /*
4718  * Based on:
4719  * Ext JS Library 1.1.1
4720  * Copyright(c) 2006-2007, Ext JS, LLC.
4721  *
4722  * Originally Released Under LGPL - original licence link has changed is not relivant.
4723  *
4724  * Fork - LGPL
4725  * <script type="text/javascript">
4726  */
4727
4728 /**
4729  * @class Roo.JsonView
4730  * @extends Roo.View
4731  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
4732 <pre><code>
4733 var view = new Roo.JsonView({
4734     container: "my-element",
4735     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
4736     multiSelect: true, 
4737     jsonRoot: "data" 
4738 });
4739
4740 // listen for node click?
4741 view.on("click", function(vw, index, node, e){
4742     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
4743 });
4744
4745 // direct load of JSON data
4746 view.load("foobar.php");
4747
4748 // Example from my blog list
4749 var tpl = new Roo.Template(
4750     '&lt;div class="entry"&gt;' +
4751     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
4752     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
4753     "&lt;/div&gt;&lt;hr /&gt;"
4754 );
4755
4756 var moreView = new Roo.JsonView({
4757     container :  "entry-list", 
4758     template : tpl,
4759     jsonRoot: "posts"
4760 });
4761 moreView.on("beforerender", this.sortEntries, this);
4762 moreView.load({
4763     url: "/blog/get-posts.php",
4764     params: "allposts=true",
4765     text: "Loading Blog Entries..."
4766 });
4767 </code></pre>
4768
4769 * Note: old code is supported with arguments : (container, template, config)
4770
4771
4772  * @constructor
4773  * Create a new JsonView
4774  * 
4775  * @param {Object} config The config object
4776  * 
4777  */
4778 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
4779     
4780     
4781     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
4782
4783     var um = this.el.getUpdateManager();
4784     um.setRenderer(this);
4785     um.on("update", this.onLoad, this);
4786     um.on("failure", this.onLoadException, this);
4787
4788     /**
4789      * @event beforerender
4790      * Fires before rendering of the downloaded JSON data.
4791      * @param {Roo.JsonView} this
4792      * @param {Object} data The JSON data loaded
4793      */
4794     /**
4795      * @event load
4796      * Fires when data is loaded.
4797      * @param {Roo.JsonView} this
4798      * @param {Object} data The JSON data loaded
4799      * @param {Object} response The raw Connect response object
4800      */
4801     /**
4802      * @event loadexception
4803      * Fires when loading fails.
4804      * @param {Roo.JsonView} this
4805      * @param {Object} response The raw Connect response object
4806      */
4807     this.addEvents({
4808         'beforerender' : true,
4809         'load' : true,
4810         'loadexception' : true
4811     });
4812 };
4813 Roo.extend(Roo.JsonView, Roo.View, {
4814     /**
4815      * @type {String} The root property in the loaded JSON object that contains the data
4816      */
4817     jsonRoot : "",
4818
4819     /**
4820      * Refreshes the view.
4821      */
4822     refresh : function(){
4823         this.clearSelections();
4824         this.el.update("");
4825         var html = [];
4826         var o = this.jsonData;
4827         if(o && o.length > 0){
4828             for(var i = 0, len = o.length; i < len; i++){
4829                 var data = this.prepareData(o[i], i, o);
4830                 html[html.length] = this.tpl.apply(data);
4831             }
4832         }else{
4833             html.push(this.emptyText);
4834         }
4835         this.el.update(html.join(""));
4836         this.nodes = this.el.dom.childNodes;
4837         this.updateIndexes(0);
4838     },
4839
4840     /**
4841      * 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.
4842      * @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:
4843      <pre><code>
4844      view.load({
4845          url: "your-url.php",
4846          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
4847          callback: yourFunction,
4848          scope: yourObject, //(optional scope)
4849          discardUrl: false,
4850          nocache: false,
4851          text: "Loading...",
4852          timeout: 30,
4853          scripts: false
4854      });
4855      </code></pre>
4856      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
4857      * 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.
4858      * @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}
4859      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
4860      * @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.
4861      */
4862     load : function(){
4863         var um = this.el.getUpdateManager();
4864         um.update.apply(um, arguments);
4865     },
4866
4867     // note - render is a standard framework call...
4868     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
4869     render : function(el, response){
4870         
4871         this.clearSelections();
4872         this.el.update("");
4873         var o;
4874         try{
4875             if (response != '') {
4876                 o = Roo.util.JSON.decode(response.responseText);
4877                 if(this.jsonRoot){
4878                     
4879                     o = o[this.jsonRoot];
4880                 }
4881             }
4882         } catch(e){
4883         }
4884         /**
4885          * The current JSON data or null
4886          */
4887         this.jsonData = o;
4888         this.beforeRender();
4889         this.refresh();
4890     },
4891
4892 /**
4893  * Get the number of records in the current JSON dataset
4894  * @return {Number}
4895  */
4896     getCount : function(){
4897         return this.jsonData ? this.jsonData.length : 0;
4898     },
4899
4900 /**
4901  * Returns the JSON object for the specified node(s)
4902  * @param {HTMLElement/Array} node The node or an array of nodes
4903  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
4904  * you get the JSON object for the node
4905  */
4906     getNodeData : function(node){
4907         if(node instanceof Array){
4908             var data = [];
4909             for(var i = 0, len = node.length; i < len; i++){
4910                 data.push(this.getNodeData(node[i]));
4911             }
4912             return data;
4913         }
4914         return this.jsonData[this.indexOf(node)] || null;
4915     },
4916
4917     beforeRender : function(){
4918         this.snapshot = this.jsonData;
4919         if(this.sortInfo){
4920             this.sort.apply(this, this.sortInfo);
4921         }
4922         this.fireEvent("beforerender", this, this.jsonData);
4923     },
4924
4925     onLoad : function(el, o){
4926         this.fireEvent("load", this, this.jsonData, o);
4927     },
4928
4929     onLoadException : function(el, o){
4930         this.fireEvent("loadexception", this, o);
4931     },
4932
4933 /**
4934  * Filter the data by a specific property.
4935  * @param {String} property A property on your JSON objects
4936  * @param {String/RegExp} value Either string that the property values
4937  * should start with, or a RegExp to test against the property
4938  */
4939     filter : function(property, value){
4940         if(this.jsonData){
4941             var data = [];
4942             var ss = this.snapshot;
4943             if(typeof value == "string"){
4944                 var vlen = value.length;
4945                 if(vlen == 0){
4946                     this.clearFilter();
4947                     return;
4948                 }
4949                 value = value.toLowerCase();
4950                 for(var i = 0, len = ss.length; i < len; i++){
4951                     var o = ss[i];
4952                     if(o[property].substr(0, vlen).toLowerCase() == value){
4953                         data.push(o);
4954                     }
4955                 }
4956             } else if(value.exec){ // regex?
4957                 for(var i = 0, len = ss.length; i < len; i++){
4958                     var o = ss[i];
4959                     if(value.test(o[property])){
4960                         data.push(o);
4961                     }
4962                 }
4963             } else{
4964                 return;
4965             }
4966             this.jsonData = data;
4967             this.refresh();
4968         }
4969     },
4970
4971 /**
4972  * Filter by a function. The passed function will be called with each
4973  * object in the current dataset. If the function returns true the value is kept,
4974  * otherwise it is filtered.
4975  * @param {Function} fn
4976  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
4977  */
4978     filterBy : function(fn, scope){
4979         if(this.jsonData){
4980             var data = [];
4981             var ss = this.snapshot;
4982             for(var i = 0, len = ss.length; i < len; i++){
4983                 var o = ss[i];
4984                 if(fn.call(scope || this, o)){
4985                     data.push(o);
4986                 }
4987             }
4988             this.jsonData = data;
4989             this.refresh();
4990         }
4991     },
4992
4993 /**
4994  * Clears the current filter.
4995  */
4996     clearFilter : function(){
4997         if(this.snapshot && this.jsonData != this.snapshot){
4998             this.jsonData = this.snapshot;
4999             this.refresh();
5000         }
5001     },
5002
5003
5004 /**
5005  * Sorts the data for this view and refreshes it.
5006  * @param {String} property A property on your JSON objects to sort on
5007  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
5008  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
5009  */
5010     sort : function(property, dir, sortType){
5011         this.sortInfo = Array.prototype.slice.call(arguments, 0);
5012         if(this.jsonData){
5013             var p = property;
5014             var dsc = dir && dir.toLowerCase() == "desc";
5015             var f = function(o1, o2){
5016                 var v1 = sortType ? sortType(o1[p]) : o1[p];
5017                 var v2 = sortType ? sortType(o2[p]) : o2[p];
5018                 ;
5019                 if(v1 < v2){
5020                     return dsc ? +1 : -1;
5021                 } else if(v1 > v2){
5022                     return dsc ? -1 : +1;
5023                 } else{
5024                     return 0;
5025                 }
5026             };
5027             this.jsonData.sort(f);
5028             this.refresh();
5029             if(this.jsonData != this.snapshot){
5030                 this.snapshot.sort(f);
5031             }
5032         }
5033     }
5034 });/*
5035  * Based on:
5036  * Ext JS Library 1.1.1
5037  * Copyright(c) 2006-2007, Ext JS, LLC.
5038  *
5039  * Originally Released Under LGPL - original licence link has changed is not relivant.
5040  *
5041  * Fork - LGPL
5042  * <script type="text/javascript">
5043  */
5044  
5045
5046 /**
5047  * @class Roo.ColorPalette
5048  * @extends Roo.Component
5049  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
5050  * Here's an example of typical usage:
5051  * <pre><code>
5052 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
5053 cp.render('my-div');
5054
5055 cp.on('select', function(palette, selColor){
5056     // do something with selColor
5057 });
5058 </code></pre>
5059  * @constructor
5060  * Create a new ColorPalette
5061  * @param {Object} config The config object
5062  */
5063 Roo.ColorPalette = function(config){
5064     Roo.ColorPalette.superclass.constructor.call(this, config);
5065     this.addEvents({
5066         /**
5067              * @event select
5068              * Fires when a color is selected
5069              * @param {ColorPalette} this
5070              * @param {String} color The 6-digit color hex code (without the # symbol)
5071              */
5072         select: true
5073     });
5074
5075     if(this.handler){
5076         this.on("select", this.handler, this.scope, true);
5077     }
5078 };
5079 Roo.extend(Roo.ColorPalette, Roo.Component, {
5080     /**
5081      * @cfg {String} itemCls
5082      * The CSS class to apply to the containing element (defaults to "x-color-palette")
5083      */
5084     itemCls : "x-color-palette",
5085     /**
5086      * @cfg {String} value
5087      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
5088      * the hex codes are case-sensitive.
5089      */
5090     value : null,
5091     clickEvent:'click',
5092     // private
5093     ctype: "Roo.ColorPalette",
5094
5095     /**
5096      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
5097      */
5098     allowReselect : false,
5099
5100     /**
5101      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
5102      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
5103      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
5104      * of colors with the width setting until the box is symmetrical.</p>
5105      * <p>You can override individual colors if needed:</p>
5106      * <pre><code>
5107 var cp = new Roo.ColorPalette();
5108 cp.colors[0] = "FF0000";  // change the first box to red
5109 </code></pre>
5110
5111 Or you can provide a custom array of your own for complete control:
5112 <pre><code>
5113 var cp = new Roo.ColorPalette();
5114 cp.colors = ["000000", "993300", "333300"];
5115 </code></pre>
5116      * @type Array
5117      */
5118     colors : [
5119         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
5120         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
5121         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
5122         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
5123         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
5124     ],
5125
5126     // private
5127     onRender : function(container, position){
5128         var t = new Roo.MasterTemplate(
5129             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
5130         );
5131         var c = this.colors;
5132         for(var i = 0, len = c.length; i < len; i++){
5133             t.add([c[i]]);
5134         }
5135         var el = document.createElement("div");
5136         el.className = this.itemCls;
5137         t.overwrite(el);
5138         container.dom.insertBefore(el, position);
5139         this.el = Roo.get(el);
5140         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
5141         if(this.clickEvent != 'click'){
5142             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
5143         }
5144     },
5145
5146     // private
5147     afterRender : function(){
5148         Roo.ColorPalette.superclass.afterRender.call(this);
5149         if(this.value){
5150             var s = this.value;
5151             this.value = null;
5152             this.select(s);
5153         }
5154     },
5155
5156     // private
5157     handleClick : function(e, t){
5158         e.preventDefault();
5159         if(!this.disabled){
5160             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
5161             this.select(c.toUpperCase());
5162         }
5163     },
5164
5165     /**
5166      * Selects the specified color in the palette (fires the select event)
5167      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
5168      */
5169     select : function(color){
5170         color = color.replace("#", "");
5171         if(color != this.value || this.allowReselect){
5172             var el = this.el;
5173             if(this.value){
5174                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
5175             }
5176             el.child("a.color-"+color).addClass("x-color-palette-sel");
5177             this.value = color;
5178             this.fireEvent("select", this, color);
5179         }
5180     }
5181 });/*
5182  * Based on:
5183  * Ext JS Library 1.1.1
5184  * Copyright(c) 2006-2007, Ext JS, LLC.
5185  *
5186  * Originally Released Under LGPL - original licence link has changed is not relivant.
5187  *
5188  * Fork - LGPL
5189  * <script type="text/javascript">
5190  */
5191  
5192 /**
5193  * @class Roo.DatePicker
5194  * @extends Roo.Component
5195  * Simple date picker class.
5196  * @constructor
5197  * Create a new DatePicker
5198  * @param {Object} config The config object
5199  */
5200 Roo.DatePicker = function(config){
5201     Roo.DatePicker.superclass.constructor.call(this, config);
5202
5203     this.value = config && config.value ?
5204                  config.value.clearTime() : new Date().clearTime();
5205
5206     this.addEvents({
5207         /**
5208              * @event select
5209              * Fires when a date is selected
5210              * @param {DatePicker} this
5211              * @param {Date} date The selected date
5212              */
5213         'select': true,
5214         /**
5215              * @event monthchange
5216              * Fires when the displayed month changes 
5217              * @param {DatePicker} this
5218              * @param {Date} date The selected month
5219              */
5220         'monthchange': true
5221     });
5222
5223     if(this.handler){
5224         this.on("select", this.handler,  this.scope || this);
5225     }
5226     // build the disabledDatesRE
5227     if(!this.disabledDatesRE && this.disabledDates){
5228         var dd = this.disabledDates;
5229         var re = "(?:";
5230         for(var i = 0; i < dd.length; i++){
5231             re += dd[i];
5232             if(i != dd.length-1) {
5233                 re += "|";
5234             }
5235         }
5236         this.disabledDatesRE = new RegExp(re + ")");
5237     }
5238 };
5239
5240 Roo.extend(Roo.DatePicker, Roo.Component, {
5241     /**
5242      * @cfg {String} todayText
5243      * The text to display on the button that selects the current date (defaults to "Today")
5244      */
5245     todayText : "Today",
5246     /**
5247      * @cfg {String} okText
5248      * The text to display on the ok button
5249      */
5250     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
5251     /**
5252      * @cfg {String} cancelText
5253      * The text to display on the cancel button
5254      */
5255     cancelText : "Cancel",
5256     /**
5257      * @cfg {String} todayTip
5258      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
5259      */
5260     todayTip : "{0} (Spacebar)",
5261     /**
5262      * @cfg {Date} minDate
5263      * Minimum allowable date (JavaScript date object, defaults to null)
5264      */
5265     minDate : null,
5266     /**
5267      * @cfg {Date} maxDate
5268      * Maximum allowable date (JavaScript date object, defaults to null)
5269      */
5270     maxDate : null,
5271     /**
5272      * @cfg {String} minText
5273      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
5274      */
5275     minText : "This date is before the minimum date",
5276     /**
5277      * @cfg {String} maxText
5278      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
5279      */
5280     maxText : "This date is after the maximum date",
5281     /**
5282      * @cfg {String} format
5283      * The default date format string which can be overriden for localization support.  The format must be
5284      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
5285      */
5286     format : "m/d/y",
5287     /**
5288      * @cfg {Array} disabledDays
5289      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
5290      */
5291     disabledDays : null,
5292     /**
5293      * @cfg {String} disabledDaysText
5294      * The tooltip to display when the date falls on a disabled day (defaults to "")
5295      */
5296     disabledDaysText : "",
5297     /**
5298      * @cfg {RegExp} disabledDatesRE
5299      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
5300      */
5301     disabledDatesRE : null,
5302     /**
5303      * @cfg {String} disabledDatesText
5304      * The tooltip text to display when the date falls on a disabled date (defaults to "")
5305      */
5306     disabledDatesText : "",
5307     /**
5308      * @cfg {Boolean} constrainToViewport
5309      * True to constrain the date picker to the viewport (defaults to true)
5310      */
5311     constrainToViewport : true,
5312     /**
5313      * @cfg {Array} monthNames
5314      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
5315      */
5316     monthNames : Date.monthNames,
5317     /**
5318      * @cfg {Array} dayNames
5319      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
5320      */
5321     dayNames : Date.dayNames,
5322     /**
5323      * @cfg {String} nextText
5324      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
5325      */
5326     nextText: 'Next Month (Control+Right)',
5327     /**
5328      * @cfg {String} prevText
5329      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
5330      */
5331     prevText: 'Previous Month (Control+Left)',
5332     /**
5333      * @cfg {String} monthYearText
5334      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
5335      */
5336     monthYearText: 'Choose a month (Control+Up/Down to move years)',
5337     /**
5338      * @cfg {Number} startDay
5339      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
5340      */
5341     startDay : 0,
5342     /**
5343      * @cfg {Bool} showClear
5344      * Show a clear button (usefull for date form elements that can be blank.)
5345      */
5346     
5347     showClear: false,
5348     
5349     /**
5350      * Sets the value of the date field
5351      * @param {Date} value The date to set
5352      */
5353     setValue : function(value){
5354         var old = this.value;
5355         
5356         if (typeof(value) == 'string') {
5357          
5358             value = Date.parseDate(value, this.format);
5359         }
5360         if (!value) {
5361             value = new Date();
5362         }
5363         
5364         this.value = value.clearTime(true);
5365         if(this.el){
5366             this.update(this.value);
5367         }
5368     },
5369
5370     /**
5371      * Gets the current selected value of the date field
5372      * @return {Date} The selected date
5373      */
5374     getValue : function(){
5375         return this.value;
5376     },
5377
5378     // private
5379     focus : function(){
5380         if(this.el){
5381             this.update(this.activeDate);
5382         }
5383     },
5384
5385     // privateval
5386     onRender : function(container, position){
5387         
5388         var m = [
5389              '<table cellspacing="0">',
5390                 '<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>',
5391                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
5392         var dn = this.dayNames;
5393         for(var i = 0; i < 7; i++){
5394             var d = this.startDay+i;
5395             if(d > 6){
5396                 d = d-7;
5397             }
5398             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
5399         }
5400         m[m.length] = "</tr></thead><tbody><tr>";
5401         for(var i = 0; i < 42; i++) {
5402             if(i % 7 == 0 && i != 0){
5403                 m[m.length] = "</tr><tr>";
5404             }
5405             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
5406         }
5407         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
5408             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
5409
5410         var el = document.createElement("div");
5411         el.className = "x-date-picker";
5412         el.innerHTML = m.join("");
5413
5414         container.dom.insertBefore(el, position);
5415
5416         this.el = Roo.get(el);
5417         this.eventEl = Roo.get(el.firstChild);
5418
5419         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
5420             handler: this.showPrevMonth,
5421             scope: this,
5422             preventDefault:true,
5423             stopDefault:true
5424         });
5425
5426         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
5427             handler: this.showNextMonth,
5428             scope: this,
5429             preventDefault:true,
5430             stopDefault:true
5431         });
5432
5433         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
5434
5435         this.monthPicker = this.el.down('div.x-date-mp');
5436         this.monthPicker.enableDisplayMode('block');
5437         
5438         var kn = new Roo.KeyNav(this.eventEl, {
5439             "left" : function(e){
5440                 e.ctrlKey ?
5441                     this.showPrevMonth() :
5442                     this.update(this.activeDate.add("d", -1));
5443             },
5444
5445             "right" : function(e){
5446                 e.ctrlKey ?
5447                     this.showNextMonth() :
5448                     this.update(this.activeDate.add("d", 1));
5449             },
5450
5451             "up" : function(e){
5452                 e.ctrlKey ?
5453                     this.showNextYear() :
5454                     this.update(this.activeDate.add("d", -7));
5455             },
5456
5457             "down" : function(e){
5458                 e.ctrlKey ?
5459                     this.showPrevYear() :
5460                     this.update(this.activeDate.add("d", 7));
5461             },
5462
5463             "pageUp" : function(e){
5464                 this.showNextMonth();
5465             },
5466
5467             "pageDown" : function(e){
5468                 this.showPrevMonth();
5469             },
5470
5471             "enter" : function(e){
5472                 e.stopPropagation();
5473                 return true;
5474             },
5475
5476             scope : this
5477         });
5478
5479         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
5480
5481         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
5482
5483         this.el.unselectable();
5484         
5485         this.cells = this.el.select("table.x-date-inner tbody td");
5486         this.textNodes = this.el.query("table.x-date-inner tbody span");
5487
5488         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
5489             text: "&#160;",
5490             tooltip: this.monthYearText
5491         });
5492
5493         this.mbtn.on('click', this.showMonthPicker, this);
5494         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
5495
5496
5497         var today = (new Date()).dateFormat(this.format);
5498         
5499         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
5500         if (this.showClear) {
5501             baseTb.add( new Roo.Toolbar.Fill());
5502         }
5503         baseTb.add({
5504             text: String.format(this.todayText, today),
5505             tooltip: String.format(this.todayTip, today),
5506             handler: this.selectToday,
5507             scope: this
5508         });
5509         
5510         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
5511             
5512         //});
5513         if (this.showClear) {
5514             
5515             baseTb.add( new Roo.Toolbar.Fill());
5516             baseTb.add({
5517                 text: '&#160;',
5518                 cls: 'x-btn-icon x-btn-clear',
5519                 handler: function() {
5520                     //this.value = '';
5521                     this.fireEvent("select", this, '');
5522                 },
5523                 scope: this
5524             });
5525         }
5526         
5527         
5528         if(Roo.isIE){
5529             this.el.repaint();
5530         }
5531         this.update(this.value);
5532     },
5533
5534     createMonthPicker : function(){
5535         if(!this.monthPicker.dom.firstChild){
5536             var buf = ['<table border="0" cellspacing="0">'];
5537             for(var i = 0; i < 6; i++){
5538                 buf.push(
5539                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
5540                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
5541                     i == 0 ?
5542                     '<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>' :
5543                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
5544                 );
5545             }
5546             buf.push(
5547                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
5548                     this.okText,
5549                     '</button><button type="button" class="x-date-mp-cancel">',
5550                     this.cancelText,
5551                     '</button></td></tr>',
5552                 '</table>'
5553             );
5554             this.monthPicker.update(buf.join(''));
5555             this.monthPicker.on('click', this.onMonthClick, this);
5556             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
5557
5558             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
5559             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
5560
5561             this.mpMonths.each(function(m, a, i){
5562                 i += 1;
5563                 if((i%2) == 0){
5564                     m.dom.xmonth = 5 + Math.round(i * .5);
5565                 }else{
5566                     m.dom.xmonth = Math.round((i-1) * .5);
5567                 }
5568             });
5569         }
5570     },
5571
5572     showMonthPicker : function(){
5573         this.createMonthPicker();
5574         var size = this.el.getSize();
5575         this.monthPicker.setSize(size);
5576         this.monthPicker.child('table').setSize(size);
5577
5578         this.mpSelMonth = (this.activeDate || this.value).getMonth();
5579         this.updateMPMonth(this.mpSelMonth);
5580         this.mpSelYear = (this.activeDate || this.value).getFullYear();
5581         this.updateMPYear(this.mpSelYear);
5582
5583         this.monthPicker.slideIn('t', {duration:.2});
5584     },
5585
5586     updateMPYear : function(y){
5587         this.mpyear = y;
5588         var ys = this.mpYears.elements;
5589         for(var i = 1; i <= 10; i++){
5590             var td = ys[i-1], y2;
5591             if((i%2) == 0){
5592                 y2 = y + Math.round(i * .5);
5593                 td.firstChild.innerHTML = y2;
5594                 td.xyear = y2;
5595             }else{
5596                 y2 = y - (5-Math.round(i * .5));
5597                 td.firstChild.innerHTML = y2;
5598                 td.xyear = y2;
5599             }
5600             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
5601         }
5602     },
5603
5604     updateMPMonth : function(sm){
5605         this.mpMonths.each(function(m, a, i){
5606             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
5607         });
5608     },
5609
5610     selectMPMonth: function(m){
5611         
5612     },
5613
5614     onMonthClick : function(e, t){
5615         e.stopEvent();
5616         var el = new Roo.Element(t), pn;
5617         if(el.is('button.x-date-mp-cancel')){
5618             this.hideMonthPicker();
5619         }
5620         else if(el.is('button.x-date-mp-ok')){
5621             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5622             this.hideMonthPicker();
5623         }
5624         else if(pn = el.up('td.x-date-mp-month', 2)){
5625             this.mpMonths.removeClass('x-date-mp-sel');
5626             pn.addClass('x-date-mp-sel');
5627             this.mpSelMonth = pn.dom.xmonth;
5628         }
5629         else if(pn = el.up('td.x-date-mp-year', 2)){
5630             this.mpYears.removeClass('x-date-mp-sel');
5631             pn.addClass('x-date-mp-sel');
5632             this.mpSelYear = pn.dom.xyear;
5633         }
5634         else if(el.is('a.x-date-mp-prev')){
5635             this.updateMPYear(this.mpyear-10);
5636         }
5637         else if(el.is('a.x-date-mp-next')){
5638             this.updateMPYear(this.mpyear+10);
5639         }
5640     },
5641
5642     onMonthDblClick : function(e, t){
5643         e.stopEvent();
5644         var el = new Roo.Element(t), pn;
5645         if(pn = el.up('td.x-date-mp-month', 2)){
5646             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
5647             this.hideMonthPicker();
5648         }
5649         else if(pn = el.up('td.x-date-mp-year', 2)){
5650             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5651             this.hideMonthPicker();
5652         }
5653     },
5654
5655     hideMonthPicker : function(disableAnim){
5656         if(this.monthPicker){
5657             if(disableAnim === true){
5658                 this.monthPicker.hide();
5659             }else{
5660                 this.monthPicker.slideOut('t', {duration:.2});
5661             }
5662         }
5663     },
5664
5665     // private
5666     showPrevMonth : function(e){
5667         this.update(this.activeDate.add("mo", -1));
5668     },
5669
5670     // private
5671     showNextMonth : function(e){
5672         this.update(this.activeDate.add("mo", 1));
5673     },
5674
5675     // private
5676     showPrevYear : function(){
5677         this.update(this.activeDate.add("y", -1));
5678     },
5679
5680     // private
5681     showNextYear : function(){
5682         this.update(this.activeDate.add("y", 1));
5683     },
5684
5685     // private
5686     handleMouseWheel : function(e){
5687         var delta = e.getWheelDelta();
5688         if(delta > 0){
5689             this.showPrevMonth();
5690             e.stopEvent();
5691         } else if(delta < 0){
5692             this.showNextMonth();
5693             e.stopEvent();
5694         }
5695     },
5696
5697     // private
5698     handleDateClick : function(e, t){
5699         e.stopEvent();
5700         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
5701             this.setValue(new Date(t.dateValue));
5702             this.fireEvent("select", this, this.value);
5703         }
5704     },
5705
5706     // private
5707     selectToday : function(){
5708         this.setValue(new Date().clearTime());
5709         this.fireEvent("select", this, this.value);
5710     },
5711
5712     // private
5713     update : function(date)
5714     {
5715         var vd = this.activeDate;
5716         this.activeDate = date;
5717         if(vd && this.el){
5718             var t = date.getTime();
5719             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
5720                 this.cells.removeClass("x-date-selected");
5721                 this.cells.each(function(c){
5722                    if(c.dom.firstChild.dateValue == t){
5723                        c.addClass("x-date-selected");
5724                        setTimeout(function(){
5725                             try{c.dom.firstChild.focus();}catch(e){}
5726                        }, 50);
5727                        return false;
5728                    }
5729                 });
5730                 return;
5731             }
5732         }
5733         
5734         var days = date.getDaysInMonth();
5735         var firstOfMonth = date.getFirstDateOfMonth();
5736         var startingPos = firstOfMonth.getDay()-this.startDay;
5737
5738         if(startingPos <= this.startDay){
5739             startingPos += 7;
5740         }
5741
5742         var pm = date.add("mo", -1);
5743         var prevStart = pm.getDaysInMonth()-startingPos;
5744
5745         var cells = this.cells.elements;
5746         var textEls = this.textNodes;
5747         days += startingPos;
5748
5749         // convert everything to numbers so it's fast
5750         var day = 86400000;
5751         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
5752         var today = new Date().clearTime().getTime();
5753         var sel = date.clearTime().getTime();
5754         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
5755         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
5756         var ddMatch = this.disabledDatesRE;
5757         var ddText = this.disabledDatesText;
5758         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
5759         var ddaysText = this.disabledDaysText;
5760         var format = this.format;
5761
5762         var setCellClass = function(cal, cell){
5763             cell.title = "";
5764             var t = d.getTime();
5765             cell.firstChild.dateValue = t;
5766             if(t == today){
5767                 cell.className += " x-date-today";
5768                 cell.title = cal.todayText;
5769             }
5770             if(t == sel){
5771                 cell.className += " x-date-selected";
5772                 setTimeout(function(){
5773                     try{cell.firstChild.focus();}catch(e){}
5774                 }, 50);
5775             }
5776             // disabling
5777             if(t < min) {
5778                 cell.className = " x-date-disabled";
5779                 cell.title = cal.minText;
5780                 return;
5781             }
5782             if(t > max) {
5783                 cell.className = " x-date-disabled";
5784                 cell.title = cal.maxText;
5785                 return;
5786             }
5787             if(ddays){
5788                 if(ddays.indexOf(d.getDay()) != -1){
5789                     cell.title = ddaysText;
5790                     cell.className = " x-date-disabled";
5791                 }
5792             }
5793             if(ddMatch && format){
5794                 var fvalue = d.dateFormat(format);
5795                 if(ddMatch.test(fvalue)){
5796                     cell.title = ddText.replace("%0", fvalue);
5797                     cell.className = " x-date-disabled";
5798                 }
5799             }
5800         };
5801
5802         var i = 0;
5803         for(; i < startingPos; i++) {
5804             textEls[i].innerHTML = (++prevStart);
5805             d.setDate(d.getDate()+1);
5806             cells[i].className = "x-date-prevday";
5807             setCellClass(this, cells[i]);
5808         }
5809         for(; i < days; i++){
5810             intDay = i - startingPos + 1;
5811             textEls[i].innerHTML = (intDay);
5812             d.setDate(d.getDate()+1);
5813             cells[i].className = "x-date-active";
5814             setCellClass(this, cells[i]);
5815         }
5816         var extraDays = 0;
5817         for(; i < 42; i++) {
5818              textEls[i].innerHTML = (++extraDays);
5819              d.setDate(d.getDate()+1);
5820              cells[i].className = "x-date-nextday";
5821              setCellClass(this, cells[i]);
5822         }
5823
5824         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
5825         this.fireEvent('monthchange', this, date);
5826         
5827         if(!this.internalRender){
5828             var main = this.el.dom.firstChild;
5829             var w = main.offsetWidth;
5830             this.el.setWidth(w + this.el.getBorderWidth("lr"));
5831             Roo.fly(main).setWidth(w);
5832             this.internalRender = true;
5833             // opera does not respect the auto grow header center column
5834             // then, after it gets a width opera refuses to recalculate
5835             // without a second pass
5836             if(Roo.isOpera && !this.secondPass){
5837                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
5838                 this.secondPass = true;
5839                 this.update.defer(10, this, [date]);
5840             }
5841         }
5842         
5843         
5844     }
5845 });        /*
5846  * Based on:
5847  * Ext JS Library 1.1.1
5848  * Copyright(c) 2006-2007, Ext JS, LLC.
5849  *
5850  * Originally Released Under LGPL - original licence link has changed is not relivant.
5851  *
5852  * Fork - LGPL
5853  * <script type="text/javascript">
5854  */
5855 /**
5856  * @class Roo.TabPanel
5857  * @extends Roo.util.Observable
5858  * A lightweight tab container.
5859  * <br><br>
5860  * Usage:
5861  * <pre><code>
5862 // basic tabs 1, built from existing content
5863 var tabs = new Roo.TabPanel("tabs1");
5864 tabs.addTab("script", "View Script");
5865 tabs.addTab("markup", "View Markup");
5866 tabs.activate("script");
5867
5868 // more advanced tabs, built from javascript
5869 var jtabs = new Roo.TabPanel("jtabs");
5870 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
5871
5872 // set up the UpdateManager
5873 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
5874 var updater = tab2.getUpdateManager();
5875 updater.setDefaultUrl("ajax1.htm");
5876 tab2.on('activate', updater.refresh, updater, true);
5877
5878 // Use setUrl for Ajax loading
5879 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
5880 tab3.setUrl("ajax2.htm", null, true);
5881
5882 // Disabled tab
5883 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
5884 tab4.disable();
5885
5886 jtabs.activate("jtabs-1");
5887  * </code></pre>
5888  * @constructor
5889  * Create a new TabPanel.
5890  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
5891  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
5892  */
5893 Roo.TabPanel = function(container, config){
5894     /**
5895     * The container element for this TabPanel.
5896     * @type Roo.Element
5897     */
5898     this.el = Roo.get(container, true);
5899     if(config){
5900         if(typeof config == "boolean"){
5901             this.tabPosition = config ? "bottom" : "top";
5902         }else{
5903             Roo.apply(this, config);
5904         }
5905     }
5906     if(this.tabPosition == "bottom"){
5907         this.bodyEl = Roo.get(this.createBody(this.el.dom));
5908         this.el.addClass("x-tabs-bottom");
5909     }
5910     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
5911     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
5912     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
5913     if(Roo.isIE){
5914         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
5915     }
5916     if(this.tabPosition != "bottom"){
5917         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
5918          * @type Roo.Element
5919          */
5920         this.bodyEl = Roo.get(this.createBody(this.el.dom));
5921         this.el.addClass("x-tabs-top");
5922     }
5923     this.items = [];
5924
5925     this.bodyEl.setStyle("position", "relative");
5926
5927     this.active = null;
5928     this.activateDelegate = this.activate.createDelegate(this);
5929
5930     this.addEvents({
5931         /**
5932          * @event tabchange
5933          * Fires when the active tab changes
5934          * @param {Roo.TabPanel} this
5935          * @param {Roo.TabPanelItem} activePanel The new active tab
5936          */
5937         "tabchange": true,
5938         /**
5939          * @event beforetabchange
5940          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
5941          * @param {Roo.TabPanel} this
5942          * @param {Object} e Set cancel to true on this object to cancel the tab change
5943          * @param {Roo.TabPanelItem} tab The tab being changed to
5944          */
5945         "beforetabchange" : true
5946     });
5947
5948     Roo.EventManager.onWindowResize(this.onResize, this);
5949     this.cpad = this.el.getPadding("lr");
5950     this.hiddenCount = 0;
5951
5952
5953     // toolbar on the tabbar support...
5954     if (this.toolbar) {
5955         var tcfg = this.toolbar;
5956         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
5957         this.toolbar = new Roo.Toolbar(tcfg);
5958         if (Roo.isSafari) {
5959             var tbl = tcfg.container.child('table', true);
5960             tbl.setAttribute('width', '100%');
5961         }
5962         
5963     }
5964    
5965
5966
5967     Roo.TabPanel.superclass.constructor.call(this);
5968 };
5969
5970 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
5971     /*
5972      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
5973      */
5974     tabPosition : "top",
5975     /*
5976      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
5977      */
5978     currentTabWidth : 0,
5979     /*
5980      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
5981      */
5982     minTabWidth : 40,
5983     /*
5984      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
5985      */
5986     maxTabWidth : 250,
5987     /*
5988      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
5989      */
5990     preferredTabWidth : 175,
5991     /*
5992      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
5993      */
5994     resizeTabs : false,
5995     /*
5996      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
5997      */
5998     monitorResize : true,
5999     /*
6000      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
6001      */
6002     toolbar : false,
6003
6004     /**
6005      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
6006      * @param {String} id The id of the div to use <b>or create</b>
6007      * @param {String} text The text for the tab
6008      * @param {String} content (optional) Content to put in the TabPanelItem body
6009      * @param {Boolean} closable (optional) True to create a close icon on the tab
6010      * @return {Roo.TabPanelItem} The created TabPanelItem
6011      */
6012     addTab : function(id, text, content, closable){
6013         var item = new Roo.TabPanelItem(this, id, text, closable);
6014         this.addTabItem(item);
6015         if(content){
6016             item.setContent(content);
6017         }
6018         return item;
6019     },
6020
6021     /**
6022      * Returns the {@link Roo.TabPanelItem} with the specified id/index
6023      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
6024      * @return {Roo.TabPanelItem}
6025      */
6026     getTab : function(id){
6027         return this.items[id];
6028     },
6029
6030     /**
6031      * Hides the {@link Roo.TabPanelItem} with the specified id/index
6032      * @param {String/Number} id The id or index of the TabPanelItem to hide.
6033      */
6034     hideTab : function(id){
6035         var t = this.items[id];
6036         if(!t.isHidden()){
6037            t.setHidden(true);
6038            this.hiddenCount++;
6039            this.autoSizeTabs();
6040         }
6041     },
6042
6043     /**
6044      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
6045      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
6046      */
6047     unhideTab : function(id){
6048         var t = this.items[id];
6049         if(t.isHidden()){
6050            t.setHidden(false);
6051            this.hiddenCount--;
6052            this.autoSizeTabs();
6053         }
6054     },
6055
6056     /**
6057      * Adds an existing {@link Roo.TabPanelItem}.
6058      * @param {Roo.TabPanelItem} item The TabPanelItem to add
6059      */
6060     addTabItem : function(item){
6061         this.items[item.id] = item;
6062         this.items.push(item);
6063         if(this.resizeTabs){
6064            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
6065            this.autoSizeTabs();
6066         }else{
6067             item.autoSize();
6068         }
6069     },
6070
6071     /**
6072      * Removes a {@link Roo.TabPanelItem}.
6073      * @param {String/Number} id The id or index of the TabPanelItem to remove.
6074      */
6075     removeTab : function(id){
6076         var items = this.items;
6077         var tab = items[id];
6078         if(!tab) { return; }
6079         var index = items.indexOf(tab);
6080         if(this.active == tab && items.length > 1){
6081             var newTab = this.getNextAvailable(index);
6082             if(newTab) {
6083                 newTab.activate();
6084             }
6085         }
6086         this.stripEl.dom.removeChild(tab.pnode.dom);
6087         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
6088             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
6089         }
6090         items.splice(index, 1);
6091         delete this.items[tab.id];
6092         tab.fireEvent("close", tab);
6093         tab.purgeListeners();
6094         this.autoSizeTabs();
6095     },
6096
6097     getNextAvailable : function(start){
6098         var items = this.items;
6099         var index = start;
6100         // look for a next tab that will slide over to
6101         // replace the one being removed
6102         while(index < items.length){
6103             var item = items[++index];
6104             if(item && !item.isHidden()){
6105                 return item;
6106             }
6107         }
6108         // if one isn't found select the previous tab (on the left)
6109         index = start;
6110         while(index >= 0){
6111             var item = items[--index];
6112             if(item && !item.isHidden()){
6113                 return item;
6114             }
6115         }
6116         return null;
6117     },
6118
6119     /**
6120      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
6121      * @param {String/Number} id The id or index of the TabPanelItem to disable.
6122      */
6123     disableTab : function(id){
6124         var tab = this.items[id];
6125         if(tab && this.active != tab){
6126             tab.disable();
6127         }
6128     },
6129
6130     /**
6131      * Enables a {@link Roo.TabPanelItem} that is disabled.
6132      * @param {String/Number} id The id or index of the TabPanelItem to enable.
6133      */
6134     enableTab : function(id){
6135         var tab = this.items[id];
6136         tab.enable();
6137     },
6138
6139     /**
6140      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
6141      * @param {String/Number} id The id or index of the TabPanelItem to activate.
6142      * @return {Roo.TabPanelItem} The TabPanelItem.
6143      */
6144     activate : function(id){
6145         var tab = this.items[id];
6146         if(!tab){
6147             return null;
6148         }
6149         if(tab == this.active || tab.disabled){
6150             return tab;
6151         }
6152         var e = {};
6153         this.fireEvent("beforetabchange", this, e, tab);
6154         if(e.cancel !== true && !tab.disabled){
6155             if(this.active){
6156                 this.active.hide();
6157             }
6158             this.active = this.items[id];
6159             this.active.show();
6160             this.fireEvent("tabchange", this, this.active);
6161         }
6162         return tab;
6163     },
6164
6165     /**
6166      * Gets the active {@link Roo.TabPanelItem}.
6167      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
6168      */
6169     getActiveTab : function(){
6170         return this.active;
6171     },
6172
6173     /**
6174      * Updates the tab body element to fit the height of the container element
6175      * for overflow scrolling
6176      * @param {Number} targetHeight (optional) Override the starting height from the elements height
6177      */
6178     syncHeight : function(targetHeight){
6179         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
6180         var bm = this.bodyEl.getMargins();
6181         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
6182         this.bodyEl.setHeight(newHeight);
6183         return newHeight;
6184     },
6185
6186     onResize : function(){
6187         if(this.monitorResize){
6188             this.autoSizeTabs();
6189         }
6190     },
6191
6192     /**
6193      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
6194      */
6195     beginUpdate : function(){
6196         this.updating = true;
6197     },
6198
6199     /**
6200      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
6201      */
6202     endUpdate : function(){
6203         this.updating = false;
6204         this.autoSizeTabs();
6205     },
6206
6207     /**
6208      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
6209      */
6210     autoSizeTabs : function(){
6211         var count = this.items.length;
6212         var vcount = count - this.hiddenCount;
6213         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
6214             return;
6215         }
6216         var w = Math.max(this.el.getWidth() - this.cpad, 10);
6217         var availWidth = Math.floor(w / vcount);
6218         var b = this.stripBody;
6219         if(b.getWidth() > w){
6220             var tabs = this.items;
6221             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
6222             if(availWidth < this.minTabWidth){
6223                 /*if(!this.sleft){    // incomplete scrolling code
6224                     this.createScrollButtons();
6225                 }
6226                 this.showScroll();
6227                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
6228             }
6229         }else{
6230             if(this.currentTabWidth < this.preferredTabWidth){
6231                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
6232             }
6233         }
6234     },
6235
6236     /**
6237      * Returns the number of tabs in this TabPanel.
6238      * @return {Number}
6239      */
6240      getCount : function(){
6241          return this.items.length;
6242      },
6243
6244     /**
6245      * Resizes all the tabs to the passed width
6246      * @param {Number} The new width
6247      */
6248     setTabWidth : function(width){
6249         this.currentTabWidth = width;
6250         for(var i = 0, len = this.items.length; i < len; i++) {
6251                 if(!this.items[i].isHidden()) {
6252                 this.items[i].setWidth(width);
6253             }
6254         }
6255     },
6256
6257     /**
6258      * Destroys this TabPanel
6259      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
6260      */
6261     destroy : function(removeEl){
6262         Roo.EventManager.removeResizeListener(this.onResize, this);
6263         for(var i = 0, len = this.items.length; i < len; i++){
6264             this.items[i].purgeListeners();
6265         }
6266         if(removeEl === true){
6267             this.el.update("");
6268             this.el.remove();
6269         }
6270     }
6271 });
6272
6273 /**
6274  * @class Roo.TabPanelItem
6275  * @extends Roo.util.Observable
6276  * Represents an individual item (tab plus body) in a TabPanel.
6277  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
6278  * @param {String} id The id of this TabPanelItem
6279  * @param {String} text The text for the tab of this TabPanelItem
6280  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
6281  */
6282 Roo.TabPanelItem = function(tabPanel, id, text, closable){
6283     /**
6284      * The {@link Roo.TabPanel} this TabPanelItem belongs to
6285      * @type Roo.TabPanel
6286      */
6287     this.tabPanel = tabPanel;
6288     /**
6289      * The id for this TabPanelItem
6290      * @type String
6291      */
6292     this.id = id;
6293     /** @private */
6294     this.disabled = false;
6295     /** @private */
6296     this.text = text;
6297     /** @private */
6298     this.loaded = false;
6299     this.closable = closable;
6300
6301     /**
6302      * The body element for this TabPanelItem.
6303      * @type Roo.Element
6304      */
6305     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
6306     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
6307     this.bodyEl.setStyle("display", "block");
6308     this.bodyEl.setStyle("zoom", "1");
6309     this.hideAction();
6310
6311     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
6312     /** @private */
6313     this.el = Roo.get(els.el, true);
6314     this.inner = Roo.get(els.inner, true);
6315     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
6316     this.pnode = Roo.get(els.el.parentNode, true);
6317     this.el.on("mousedown", this.onTabMouseDown, this);
6318     this.el.on("click", this.onTabClick, this);
6319     /** @private */
6320     if(closable){
6321         var c = Roo.get(els.close, true);
6322         c.dom.title = this.closeText;
6323         c.addClassOnOver("close-over");
6324         c.on("click", this.closeClick, this);
6325      }
6326
6327     this.addEvents({
6328          /**
6329          * @event activate
6330          * Fires when this tab becomes the active tab.
6331          * @param {Roo.TabPanel} tabPanel The parent TabPanel
6332          * @param {Roo.TabPanelItem} this
6333          */
6334         "activate": true,
6335         /**
6336          * @event beforeclose
6337          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
6338          * @param {Roo.TabPanelItem} this
6339          * @param {Object} e Set cancel to true on this object to cancel the close.
6340          */
6341         "beforeclose": true,
6342         /**
6343          * @event close
6344          * Fires when this tab is closed.
6345          * @param {Roo.TabPanelItem} this
6346          */
6347          "close": true,
6348         /**
6349          * @event deactivate
6350          * Fires when this tab is no longer the active tab.
6351          * @param {Roo.TabPanel} tabPanel The parent TabPanel
6352          * @param {Roo.TabPanelItem} this
6353          */
6354          "deactivate" : true
6355     });
6356     this.hidden = false;
6357
6358     Roo.TabPanelItem.superclass.constructor.call(this);
6359 };
6360
6361 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
6362     purgeListeners : function(){
6363        Roo.util.Observable.prototype.purgeListeners.call(this);
6364        this.el.removeAllListeners();
6365     },
6366     /**
6367      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
6368      */
6369     show : function(){
6370         this.pnode.addClass("on");
6371         this.showAction();
6372         if(Roo.isOpera){
6373             this.tabPanel.stripWrap.repaint();
6374         }
6375         this.fireEvent("activate", this.tabPanel, this);
6376     },
6377
6378     /**
6379      * Returns true if this tab is the active tab.
6380      * @return {Boolean}
6381      */
6382     isActive : function(){
6383         return this.tabPanel.getActiveTab() == this;
6384     },
6385
6386     /**
6387      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
6388      */
6389     hide : function(){
6390         this.pnode.removeClass("on");
6391         this.hideAction();
6392         this.fireEvent("deactivate", this.tabPanel, this);
6393     },
6394
6395     hideAction : function(){
6396         this.bodyEl.hide();
6397         this.bodyEl.setStyle("position", "absolute");
6398         this.bodyEl.setLeft("-20000px");
6399         this.bodyEl.setTop("-20000px");
6400     },
6401
6402     showAction : function(){
6403         this.bodyEl.setStyle("position", "relative");
6404         this.bodyEl.setTop("");
6405         this.bodyEl.setLeft("");
6406         this.bodyEl.show();
6407     },
6408
6409     /**
6410      * Set the tooltip for the tab.
6411      * @param {String} tooltip The tab's tooltip
6412      */
6413     setTooltip : function(text){
6414         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
6415             this.textEl.dom.qtip = text;
6416             this.textEl.dom.removeAttribute('title');
6417         }else{
6418             this.textEl.dom.title = text;
6419         }
6420     },
6421
6422     onTabClick : function(e){
6423         e.preventDefault();
6424         this.tabPanel.activate(this.id);
6425     },
6426
6427     onTabMouseDown : function(e){
6428         e.preventDefault();
6429         this.tabPanel.activate(this.id);
6430     },
6431
6432     getWidth : function(){
6433         return this.inner.getWidth();
6434     },
6435
6436     setWidth : function(width){
6437         var iwidth = width - this.pnode.getPadding("lr");
6438         this.inner.setWidth(iwidth);
6439         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
6440         this.pnode.setWidth(width);
6441     },
6442
6443     /**
6444      * Show or hide the tab
6445      * @param {Boolean} hidden True to hide or false to show.
6446      */
6447     setHidden : function(hidden){
6448         this.hidden = hidden;
6449         this.pnode.setStyle("display", hidden ? "none" : "");
6450     },
6451
6452     /**
6453      * Returns true if this tab is "hidden"
6454      * @return {Boolean}
6455      */
6456     isHidden : function(){
6457         return this.hidden;
6458     },
6459
6460     /**
6461      * Returns the text for this tab
6462      * @return {String}
6463      */
6464     getText : function(){
6465         return this.text;
6466     },
6467
6468     autoSize : function(){
6469         //this.el.beginMeasure();
6470         this.textEl.setWidth(1);
6471         /*
6472          *  #2804 [new] Tabs in Roojs
6473          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
6474          */
6475         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
6476         //this.el.endMeasure();
6477     },
6478
6479     /**
6480      * Sets the text for the tab (Note: this also sets the tooltip text)
6481      * @param {String} text The tab's text and tooltip
6482      */
6483     setText : function(text){
6484         this.text = text;
6485         this.textEl.update(text);
6486         this.setTooltip(text);
6487         if(!this.tabPanel.resizeTabs){
6488             this.autoSize();
6489         }
6490     },
6491     /**
6492      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
6493      */
6494     activate : function(){
6495         this.tabPanel.activate(this.id);
6496     },
6497
6498     /**
6499      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
6500      */
6501     disable : function(){
6502         if(this.tabPanel.active != this){
6503             this.disabled = true;
6504             this.pnode.addClass("disabled");
6505         }
6506     },
6507
6508     /**
6509      * Enables this TabPanelItem if it was previously disabled.
6510      */
6511     enable : function(){
6512         this.disabled = false;
6513         this.pnode.removeClass("disabled");
6514     },
6515
6516     /**
6517      * Sets the content for this TabPanelItem.
6518      * @param {String} content The content
6519      * @param {Boolean} loadScripts true to look for and load scripts
6520      */
6521     setContent : function(content, loadScripts){
6522         this.bodyEl.update(content, loadScripts);
6523     },
6524
6525     /**
6526      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
6527      * @return {Roo.UpdateManager} The UpdateManager
6528      */
6529     getUpdateManager : function(){
6530         return this.bodyEl.getUpdateManager();
6531     },
6532
6533     /**
6534      * Set a URL to be used to load the content for this TabPanelItem.
6535      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
6536      * @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)
6537      * @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)
6538      * @return {Roo.UpdateManager} The UpdateManager
6539      */
6540     setUrl : function(url, params, loadOnce){
6541         if(this.refreshDelegate){
6542             this.un('activate', this.refreshDelegate);
6543         }
6544         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
6545         this.on("activate", this.refreshDelegate);
6546         return this.bodyEl.getUpdateManager();
6547     },
6548
6549     /** @private */
6550     _handleRefresh : function(url, params, loadOnce){
6551         if(!loadOnce || !this.loaded){
6552             var updater = this.bodyEl.getUpdateManager();
6553             updater.update(url, params, this._setLoaded.createDelegate(this));
6554         }
6555     },
6556
6557     /**
6558      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
6559      *   Will fail silently if the setUrl method has not been called.
6560      *   This does not activate the panel, just updates its content.
6561      */
6562     refresh : function(){
6563         if(this.refreshDelegate){
6564            this.loaded = false;
6565            this.refreshDelegate();
6566         }
6567     },
6568
6569     /** @private */
6570     _setLoaded : function(){
6571         this.loaded = true;
6572     },
6573
6574     /** @private */
6575     closeClick : function(e){
6576         var o = {};
6577         e.stopEvent();
6578         this.fireEvent("beforeclose", this, o);
6579         if(o.cancel !== true){
6580             this.tabPanel.removeTab(this.id);
6581         }
6582     },
6583     /**
6584      * The text displayed in the tooltip for the close icon.
6585      * @type String
6586      */
6587     closeText : "Close this tab"
6588 });
6589
6590 /** @private */
6591 Roo.TabPanel.prototype.createStrip = function(container){
6592     var strip = document.createElement("div");
6593     strip.className = "x-tabs-wrap";
6594     container.appendChild(strip);
6595     return strip;
6596 };
6597 /** @private */
6598 Roo.TabPanel.prototype.createStripList = function(strip){
6599     // div wrapper for retard IE
6600     // returns the "tr" element.
6601     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
6602         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
6603         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
6604     return strip.firstChild.firstChild.firstChild.firstChild;
6605 };
6606 /** @private */
6607 Roo.TabPanel.prototype.createBody = function(container){
6608     var body = document.createElement("div");
6609     Roo.id(body, "tab-body");
6610     Roo.fly(body).addClass("x-tabs-body");
6611     container.appendChild(body);
6612     return body;
6613 };
6614 /** @private */
6615 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
6616     var body = Roo.getDom(id);
6617     if(!body){
6618         body = document.createElement("div");
6619         body.id = id;
6620     }
6621     Roo.fly(body).addClass("x-tabs-item-body");
6622     bodyEl.insertBefore(body, bodyEl.firstChild);
6623     return body;
6624 };
6625 /** @private */
6626 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
6627     var td = document.createElement("td");
6628     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
6629     //stripEl.appendChild(td);
6630     if(closable){
6631         td.className = "x-tabs-closable";
6632         if(!this.closeTpl){
6633             this.closeTpl = new Roo.Template(
6634                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6635                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
6636                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
6637             );
6638         }
6639         var el = this.closeTpl.overwrite(td, {"text": text});
6640         var close = el.getElementsByTagName("div")[0];
6641         var inner = el.getElementsByTagName("em")[0];
6642         return {"el": el, "close": close, "inner": inner};
6643     } else {
6644         if(!this.tabTpl){
6645             this.tabTpl = new Roo.Template(
6646                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6647                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
6648             );
6649         }
6650         var el = this.tabTpl.overwrite(td, {"text": text});
6651         var inner = el.getElementsByTagName("em")[0];
6652         return {"el": el, "inner": inner};
6653     }
6654 };/*
6655  * Based on:
6656  * Ext JS Library 1.1.1
6657  * Copyright(c) 2006-2007, Ext JS, LLC.
6658  *
6659  * Originally Released Under LGPL - original licence link has changed is not relivant.
6660  *
6661  * Fork - LGPL
6662  * <script type="text/javascript">
6663  */
6664
6665 /**
6666  * @class Roo.Button
6667  * @extends Roo.util.Observable
6668  * Simple Button class
6669  * @cfg {String} text The button text
6670  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
6671  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
6672  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
6673  * @cfg {Object} scope The scope of the handler
6674  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
6675  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
6676  * @cfg {Boolean} hidden True to start hidden (defaults to false)
6677  * @cfg {Boolean} disabled True to start disabled (defaults to false)
6678  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
6679  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
6680    applies if enableToggle = true)
6681  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
6682  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
6683   an {@link Roo.util.ClickRepeater} config object (defaults to false).
6684  * @constructor
6685  * Create a new button
6686  * @param {Object} config The config object
6687  */
6688 Roo.Button = function(renderTo, config)
6689 {
6690     if (!config) {
6691         config = renderTo;
6692         renderTo = config.renderTo || false;
6693     }
6694     
6695     Roo.apply(this, config);
6696     this.addEvents({
6697         /**
6698              * @event click
6699              * Fires when this button is clicked
6700              * @param {Button} this
6701              * @param {EventObject} e The click event
6702              */
6703             "click" : true,
6704         /**
6705              * @event toggle
6706              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
6707              * @param {Button} this
6708              * @param {Boolean} pressed
6709              */
6710             "toggle" : true,
6711         /**
6712              * @event mouseover
6713              * Fires when the mouse hovers over the button
6714              * @param {Button} this
6715              * @param {Event} e The event object
6716              */
6717         'mouseover' : true,
6718         /**
6719              * @event mouseout
6720              * Fires when the mouse exits the button
6721              * @param {Button} this
6722              * @param {Event} e The event object
6723              */
6724         'mouseout': true,
6725          /**
6726              * @event render
6727              * Fires when the button is rendered
6728              * @param {Button} this
6729              */
6730         'render': true
6731     });
6732     if(this.menu){
6733         this.menu = Roo.menu.MenuMgr.get(this.menu);
6734     }
6735     // register listeners first!!  - so render can be captured..
6736     Roo.util.Observable.call(this);
6737     if(renderTo){
6738         this.render(renderTo);
6739     }
6740     
6741   
6742 };
6743
6744 Roo.extend(Roo.Button, Roo.util.Observable, {
6745     /**
6746      * 
6747      */
6748     
6749     /**
6750      * Read-only. True if this button is hidden
6751      * @type Boolean
6752      */
6753     hidden : false,
6754     /**
6755      * Read-only. True if this button is disabled
6756      * @type Boolean
6757      */
6758     disabled : false,
6759     /**
6760      * Read-only. True if this button is pressed (only if enableToggle = true)
6761      * @type Boolean
6762      */
6763     pressed : false,
6764
6765     /**
6766      * @cfg {Number} tabIndex 
6767      * The DOM tabIndex for this button (defaults to undefined)
6768      */
6769     tabIndex : undefined,
6770
6771     /**
6772      * @cfg {Boolean} enableToggle
6773      * True to enable pressed/not pressed toggling (defaults to false)
6774      */
6775     enableToggle: false,
6776     /**
6777      * @cfg {Mixed} menu
6778      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
6779      */
6780     menu : undefined,
6781     /**
6782      * @cfg {String} menuAlign
6783      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
6784      */
6785     menuAlign : "tl-bl?",
6786
6787     /**
6788      * @cfg {String} iconCls
6789      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
6790      */
6791     iconCls : undefined,
6792     /**
6793      * @cfg {String} type
6794      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
6795      */
6796     type : 'button',
6797
6798     // private
6799     menuClassTarget: 'tr',
6800
6801     /**
6802      * @cfg {String} clickEvent
6803      * The type of event to map to the button's event handler (defaults to 'click')
6804      */
6805     clickEvent : 'click',
6806
6807     /**
6808      * @cfg {Boolean} handleMouseEvents
6809      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
6810      */
6811     handleMouseEvents : true,
6812
6813     /**
6814      * @cfg {String} tooltipType
6815      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
6816      */
6817     tooltipType : 'qtip',
6818
6819     /**
6820      * @cfg {String} cls
6821      * A CSS class to apply to the button's main element.
6822      */
6823     
6824     /**
6825      * @cfg {Roo.Template} template (Optional)
6826      * An {@link Roo.Template} with which to create the Button's main element. This Template must
6827      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
6828      * require code modifications if required elements (e.g. a button) aren't present.
6829      */
6830
6831     // private
6832     render : function(renderTo){
6833         var btn;
6834         if(this.hideParent){
6835             this.parentEl = Roo.get(renderTo);
6836         }
6837         if(!this.dhconfig){
6838             if(!this.template){
6839                 if(!Roo.Button.buttonTemplate){
6840                     // hideous table template
6841                     Roo.Button.buttonTemplate = new Roo.Template(
6842                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
6843                         '<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>',
6844                         "</tr></tbody></table>");
6845                 }
6846                 this.template = Roo.Button.buttonTemplate;
6847             }
6848             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
6849             var btnEl = btn.child("button:first");
6850             btnEl.on('focus', this.onFocus, this);
6851             btnEl.on('blur', this.onBlur, this);
6852             if(this.cls){
6853                 btn.addClass(this.cls);
6854             }
6855             if(this.icon){
6856                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
6857             }
6858             if(this.iconCls){
6859                 btnEl.addClass(this.iconCls);
6860                 if(!this.cls){
6861                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
6862                 }
6863             }
6864             if(this.tabIndex !== undefined){
6865                 btnEl.dom.tabIndex = this.tabIndex;
6866             }
6867             if(this.tooltip){
6868                 if(typeof this.tooltip == 'object'){
6869                     Roo.QuickTips.tips(Roo.apply({
6870                           target: btnEl.id
6871                     }, this.tooltip));
6872                 } else {
6873                     btnEl.dom[this.tooltipType] = this.tooltip;
6874                 }
6875             }
6876         }else{
6877             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
6878         }
6879         this.el = btn;
6880         if(this.id){
6881             this.el.dom.id = this.el.id = this.id;
6882         }
6883         if(this.menu){
6884             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
6885             this.menu.on("show", this.onMenuShow, this);
6886             this.menu.on("hide", this.onMenuHide, this);
6887         }
6888         btn.addClass("x-btn");
6889         if(Roo.isIE && !Roo.isIE7){
6890             this.autoWidth.defer(1, this);
6891         }else{
6892             this.autoWidth();
6893         }
6894         if(this.handleMouseEvents){
6895             btn.on("mouseover", this.onMouseOver, this);
6896             btn.on("mouseout", this.onMouseOut, this);
6897             btn.on("mousedown", this.onMouseDown, this);
6898         }
6899         btn.on(this.clickEvent, this.onClick, this);
6900         //btn.on("mouseup", this.onMouseUp, this);
6901         if(this.hidden){
6902             this.hide();
6903         }
6904         if(this.disabled){
6905             this.disable();
6906         }
6907         Roo.ButtonToggleMgr.register(this);
6908         if(this.pressed){
6909             this.el.addClass("x-btn-pressed");
6910         }
6911         if(this.repeat){
6912             var repeater = new Roo.util.ClickRepeater(btn,
6913                 typeof this.repeat == "object" ? this.repeat : {}
6914             );
6915             repeater.on("click", this.onClick,  this);
6916         }
6917         
6918         this.fireEvent('render', this);
6919         
6920     },
6921     /**
6922      * Returns the button's underlying element
6923      * @return {Roo.Element} The element
6924      */
6925     getEl : function(){
6926         return this.el;  
6927     },
6928     
6929     /**
6930      * Destroys this Button and removes any listeners.
6931      */
6932     destroy : function(){
6933         Roo.ButtonToggleMgr.unregister(this);
6934         this.el.removeAllListeners();
6935         this.purgeListeners();
6936         this.el.remove();
6937     },
6938
6939     // private
6940     autoWidth : function(){
6941         if(this.el){
6942             this.el.setWidth("auto");
6943             if(Roo.isIE7 && Roo.isStrict){
6944                 var ib = this.el.child('button');
6945                 if(ib && ib.getWidth() > 20){
6946                     ib.clip();
6947                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
6948                 }
6949             }
6950             if(this.minWidth){
6951                 if(this.hidden){
6952                     this.el.beginMeasure();
6953                 }
6954                 if(this.el.getWidth() < this.minWidth){
6955                     this.el.setWidth(this.minWidth);
6956                 }
6957                 if(this.hidden){
6958                     this.el.endMeasure();
6959                 }
6960             }
6961         }
6962     },
6963
6964     /**
6965      * Assigns this button's click handler
6966      * @param {Function} handler The function to call when the button is clicked
6967      * @param {Object} scope (optional) Scope for the function passed in
6968      */
6969     setHandler : function(handler, scope){
6970         this.handler = handler;
6971         this.scope = scope;  
6972     },
6973     
6974     /**
6975      * Sets this button's text
6976      * @param {String} text The button text
6977      */
6978     setText : function(text){
6979         this.text = text;
6980         if(this.el){
6981             this.el.child("td.x-btn-center button.x-btn-text").update(text);
6982         }
6983         this.autoWidth();
6984     },
6985     
6986     /**
6987      * Gets the text for this button
6988      * @return {String} The button text
6989      */
6990     getText : function(){
6991         return this.text;  
6992     },
6993     
6994     /**
6995      * Show this button
6996      */
6997     show: function(){
6998         this.hidden = false;
6999         if(this.el){
7000             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
7001         }
7002     },
7003     
7004     /**
7005      * Hide this button
7006      */
7007     hide: function(){
7008         this.hidden = true;
7009         if(this.el){
7010             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
7011         }
7012     },
7013     
7014     /**
7015      * Convenience function for boolean show/hide
7016      * @param {Boolean} visible True to show, false to hide
7017      */
7018     setVisible: function(visible){
7019         if(visible) {
7020             this.show();
7021         }else{
7022             this.hide();
7023         }
7024     },
7025     
7026     /**
7027      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
7028      * @param {Boolean} state (optional) Force a particular state
7029      */
7030     toggle : function(state){
7031         state = state === undefined ? !this.pressed : state;
7032         if(state != this.pressed){
7033             if(state){
7034                 this.el.addClass("x-btn-pressed");
7035                 this.pressed = true;
7036                 this.fireEvent("toggle", this, true);
7037             }else{
7038                 this.el.removeClass("x-btn-pressed");
7039                 this.pressed = false;
7040                 this.fireEvent("toggle", this, false);
7041             }
7042             if(this.toggleHandler){
7043                 this.toggleHandler.call(this.scope || this, this, state);
7044             }
7045         }
7046     },
7047     
7048     /**
7049      * Focus the button
7050      */
7051     focus : function(){
7052         this.el.child('button:first').focus();
7053     },
7054     
7055     /**
7056      * Disable this button
7057      */
7058     disable : function(){
7059         if(this.el){
7060             this.el.addClass("x-btn-disabled");
7061         }
7062         this.disabled = true;
7063     },
7064     
7065     /**
7066      * Enable this button
7067      */
7068     enable : function(){
7069         if(this.el){
7070             this.el.removeClass("x-btn-disabled");
7071         }
7072         this.disabled = false;
7073     },
7074
7075     /**
7076      * Convenience function for boolean enable/disable
7077      * @param {Boolean} enabled True to enable, false to disable
7078      */
7079     setDisabled : function(v){
7080         this[v !== true ? "enable" : "disable"]();
7081     },
7082
7083     // private
7084     onClick : function(e)
7085     {
7086         if(e){
7087             e.preventDefault();
7088         }
7089         if(e.button != 0){
7090             return;
7091         }
7092         if(!this.disabled){
7093             if(this.enableToggle){
7094                 this.toggle();
7095             }
7096             if(this.menu && !this.menu.isVisible()){
7097                 this.menu.show(this.el, this.menuAlign);
7098             }
7099             this.fireEvent("click", this, e);
7100             if(this.handler){
7101                 this.el.removeClass("x-btn-over");
7102                 this.handler.call(this.scope || this, this, e);
7103             }
7104         }
7105     },
7106     // private
7107     onMouseOver : function(e){
7108         if(!this.disabled){
7109             this.el.addClass("x-btn-over");
7110             this.fireEvent('mouseover', this, e);
7111         }
7112     },
7113     // private
7114     onMouseOut : function(e){
7115         if(!e.within(this.el,  true)){
7116             this.el.removeClass("x-btn-over");
7117             this.fireEvent('mouseout', this, e);
7118         }
7119     },
7120     // private
7121     onFocus : function(e){
7122         if(!this.disabled){
7123             this.el.addClass("x-btn-focus");
7124         }
7125     },
7126     // private
7127     onBlur : function(e){
7128         this.el.removeClass("x-btn-focus");
7129     },
7130     // private
7131     onMouseDown : function(e){
7132         if(!this.disabled && e.button == 0){
7133             this.el.addClass("x-btn-click");
7134             Roo.get(document).on('mouseup', this.onMouseUp, this);
7135         }
7136     },
7137     // private
7138     onMouseUp : function(e){
7139         if(e.button == 0){
7140             this.el.removeClass("x-btn-click");
7141             Roo.get(document).un('mouseup', this.onMouseUp, this);
7142         }
7143     },
7144     // private
7145     onMenuShow : function(e){
7146         this.el.addClass("x-btn-menu-active");
7147     },
7148     // private
7149     onMenuHide : function(e){
7150         this.el.removeClass("x-btn-menu-active");
7151     }   
7152 });
7153
7154 // Private utility class used by Button
7155 Roo.ButtonToggleMgr = function(){
7156    var groups = {};
7157    
7158    function toggleGroup(btn, state){
7159        if(state){
7160            var g = groups[btn.toggleGroup];
7161            for(var i = 0, l = g.length; i < l; i++){
7162                if(g[i] != btn){
7163                    g[i].toggle(false);
7164                }
7165            }
7166        }
7167    }
7168    
7169    return {
7170        register : function(btn){
7171            if(!btn.toggleGroup){
7172                return;
7173            }
7174            var g = groups[btn.toggleGroup];
7175            if(!g){
7176                g = groups[btn.toggleGroup] = [];
7177            }
7178            g.push(btn);
7179            btn.on("toggle", toggleGroup);
7180        },
7181        
7182        unregister : function(btn){
7183            if(!btn.toggleGroup){
7184                return;
7185            }
7186            var g = groups[btn.toggleGroup];
7187            if(g){
7188                g.remove(btn);
7189                btn.un("toggle", toggleGroup);
7190            }
7191        }
7192    };
7193 }();/*
7194  * Based on:
7195  * Ext JS Library 1.1.1
7196  * Copyright(c) 2006-2007, Ext JS, LLC.
7197  *
7198  * Originally Released Under LGPL - original licence link has changed is not relivant.
7199  *
7200  * Fork - LGPL
7201  * <script type="text/javascript">
7202  */
7203  
7204 /**
7205  * @class Roo.SplitButton
7206  * @extends Roo.Button
7207  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
7208  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
7209  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
7210  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
7211  * @cfg {String} arrowTooltip The title attribute of the arrow
7212  * @constructor
7213  * Create a new menu button
7214  * @param {String/HTMLElement/Element} renderTo The element to append the button to
7215  * @param {Object} config The config object
7216  */
7217 Roo.SplitButton = function(renderTo, config){
7218     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
7219     /**
7220      * @event arrowclick
7221      * Fires when this button's arrow is clicked
7222      * @param {SplitButton} this
7223      * @param {EventObject} e The click event
7224      */
7225     this.addEvents({"arrowclick":true});
7226 };
7227
7228 Roo.extend(Roo.SplitButton, Roo.Button, {
7229     render : function(renderTo){
7230         // this is one sweet looking template!
7231         var tpl = new Roo.Template(
7232             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
7233             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
7234             '<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>',
7235             "</tbody></table></td><td>",
7236             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
7237             '<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>',
7238             "</tbody></table></td></tr></table>"
7239         );
7240         var btn = tpl.append(renderTo, [this.text, this.type], true);
7241         var btnEl = btn.child("button");
7242         if(this.cls){
7243             btn.addClass(this.cls);
7244         }
7245         if(this.icon){
7246             btnEl.setStyle('background-image', 'url(' +this.icon +')');
7247         }
7248         if(this.iconCls){
7249             btnEl.addClass(this.iconCls);
7250             if(!this.cls){
7251                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
7252             }
7253         }
7254         this.el = btn;
7255         if(this.handleMouseEvents){
7256             btn.on("mouseover", this.onMouseOver, this);
7257             btn.on("mouseout", this.onMouseOut, this);
7258             btn.on("mousedown", this.onMouseDown, this);
7259             btn.on("mouseup", this.onMouseUp, this);
7260         }
7261         btn.on(this.clickEvent, this.onClick, this);
7262         if(this.tooltip){
7263             if(typeof this.tooltip == 'object'){
7264                 Roo.QuickTips.tips(Roo.apply({
7265                       target: btnEl.id
7266                 }, this.tooltip));
7267             } else {
7268                 btnEl.dom[this.tooltipType] = this.tooltip;
7269             }
7270         }
7271         if(this.arrowTooltip){
7272             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
7273         }
7274         if(this.hidden){
7275             this.hide();
7276         }
7277         if(this.disabled){
7278             this.disable();
7279         }
7280         if(this.pressed){
7281             this.el.addClass("x-btn-pressed");
7282         }
7283         if(Roo.isIE && !Roo.isIE7){
7284             this.autoWidth.defer(1, this);
7285         }else{
7286             this.autoWidth();
7287         }
7288         if(this.menu){
7289             this.menu.on("show", this.onMenuShow, this);
7290             this.menu.on("hide", this.onMenuHide, this);
7291         }
7292         this.fireEvent('render', this);
7293     },
7294
7295     // private
7296     autoWidth : function(){
7297         if(this.el){
7298             var tbl = this.el.child("table:first");
7299             var tbl2 = this.el.child("table:last");
7300             this.el.setWidth("auto");
7301             tbl.setWidth("auto");
7302             if(Roo.isIE7 && Roo.isStrict){
7303                 var ib = this.el.child('button:first');
7304                 if(ib && ib.getWidth() > 20){
7305                     ib.clip();
7306                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
7307                 }
7308             }
7309             if(this.minWidth){
7310                 if(this.hidden){
7311                     this.el.beginMeasure();
7312                 }
7313                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
7314                     tbl.setWidth(this.minWidth-tbl2.getWidth());
7315                 }
7316                 if(this.hidden){
7317                     this.el.endMeasure();
7318                 }
7319             }
7320             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
7321         } 
7322     },
7323     /**
7324      * Sets this button's click handler
7325      * @param {Function} handler The function to call when the button is clicked
7326      * @param {Object} scope (optional) Scope for the function passed above
7327      */
7328     setHandler : function(handler, scope){
7329         this.handler = handler;
7330         this.scope = scope;  
7331     },
7332     
7333     /**
7334      * Sets this button's arrow click handler
7335      * @param {Function} handler The function to call when the arrow is clicked
7336      * @param {Object} scope (optional) Scope for the function passed above
7337      */
7338     setArrowHandler : function(handler, scope){
7339         this.arrowHandler = handler;
7340         this.scope = scope;  
7341     },
7342     
7343     /**
7344      * Focus the button
7345      */
7346     focus : function(){
7347         if(this.el){
7348             this.el.child("button:first").focus();
7349         }
7350     },
7351
7352     // private
7353     onClick : function(e){
7354         e.preventDefault();
7355         if(!this.disabled){
7356             if(e.getTarget(".x-btn-menu-arrow-wrap")){
7357                 if(this.menu && !this.menu.isVisible()){
7358                     this.menu.show(this.el, this.menuAlign);
7359                 }
7360                 this.fireEvent("arrowclick", this, e);
7361                 if(this.arrowHandler){
7362                     this.arrowHandler.call(this.scope || this, this, e);
7363                 }
7364             }else{
7365                 this.fireEvent("click", this, e);
7366                 if(this.handler){
7367                     this.handler.call(this.scope || this, this, e);
7368                 }
7369             }
7370         }
7371     },
7372     // private
7373     onMouseDown : function(e){
7374         if(!this.disabled){
7375             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
7376         }
7377     },
7378     // private
7379     onMouseUp : function(e){
7380         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
7381     }   
7382 });
7383
7384
7385 // backwards compat
7386 Roo.MenuButton = Roo.SplitButton;/*
7387  * Based on:
7388  * Ext JS Library 1.1.1
7389  * Copyright(c) 2006-2007, Ext JS, LLC.
7390  *
7391  * Originally Released Under LGPL - original licence link has changed is not relivant.
7392  *
7393  * Fork - LGPL
7394  * <script type="text/javascript">
7395  */
7396
7397 /**
7398  * @class Roo.Toolbar
7399  * Basic Toolbar class.
7400  * @constructor
7401  * Creates a new Toolbar
7402  * @param {Object} container The config object
7403  */ 
7404 Roo.Toolbar = function(container, buttons, config)
7405 {
7406     /// old consturctor format still supported..
7407     if(container instanceof Array){ // omit the container for later rendering
7408         buttons = container;
7409         config = buttons;
7410         container = null;
7411     }
7412     if (typeof(container) == 'object' && container.xtype) {
7413         config = container;
7414         container = config.container;
7415         buttons = config.buttons || []; // not really - use items!!
7416     }
7417     var xitems = [];
7418     if (config && config.items) {
7419         xitems = config.items;
7420         delete config.items;
7421     }
7422     Roo.apply(this, config);
7423     this.buttons = buttons;
7424     
7425     if(container){
7426         this.render(container);
7427     }
7428     this.xitems = xitems;
7429     Roo.each(xitems, function(b) {
7430         this.add(b);
7431     }, this);
7432     
7433 };
7434
7435 Roo.Toolbar.prototype = {
7436     /**
7437      * @cfg {Array} items
7438      * array of button configs or elements to add (will be converted to a MixedCollection)
7439      */
7440     
7441     /**
7442      * @cfg {String/HTMLElement/Element} container
7443      * The id or element that will contain the toolbar
7444      */
7445     // private
7446     render : function(ct){
7447         this.el = Roo.get(ct);
7448         if(this.cls){
7449             this.el.addClass(this.cls);
7450         }
7451         // using a table allows for vertical alignment
7452         // 100% width is needed by Safari...
7453         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
7454         this.tr = this.el.child("tr", true);
7455         var autoId = 0;
7456         this.items = new Roo.util.MixedCollection(false, function(o){
7457             return o.id || ("item" + (++autoId));
7458         });
7459         if(this.buttons){
7460             this.add.apply(this, this.buttons);
7461             delete this.buttons;
7462         }
7463     },
7464
7465     /**
7466      * Adds element(s) to the toolbar -- this function takes a variable number of 
7467      * arguments of mixed type and adds them to the toolbar.
7468      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
7469      * <ul>
7470      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
7471      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
7472      * <li>Field: Any form field (equivalent to {@link #addField})</li>
7473      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
7474      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
7475      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
7476      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
7477      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
7478      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
7479      * </ul>
7480      * @param {Mixed} arg2
7481      * @param {Mixed} etc.
7482      */
7483     add : function(){
7484         var a = arguments, l = a.length;
7485         for(var i = 0; i < l; i++){
7486             this._add(a[i]);
7487         }
7488     },
7489     // private..
7490     _add : function(el) {
7491         
7492         if (el.xtype) {
7493             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
7494         }
7495         
7496         if (el.applyTo){ // some kind of form field
7497             return this.addField(el);
7498         } 
7499         if (el.render){ // some kind of Toolbar.Item
7500             return this.addItem(el);
7501         }
7502         if (typeof el == "string"){ // string
7503             if(el == "separator" || el == "-"){
7504                 return this.addSeparator();
7505             }
7506             if (el == " "){
7507                 return this.addSpacer();
7508             }
7509             if(el == "->"){
7510                 return this.addFill();
7511             }
7512             return this.addText(el);
7513             
7514         }
7515         if(el.tagName){ // element
7516             return this.addElement(el);
7517         }
7518         if(typeof el == "object"){ // must be button config?
7519             return this.addButton(el);
7520         }
7521         // and now what?!?!
7522         return false;
7523         
7524     },
7525     
7526     /**
7527      * Add an Xtype element
7528      * @param {Object} xtype Xtype Object
7529      * @return {Object} created Object
7530      */
7531     addxtype : function(e){
7532         return this.add(e);  
7533     },
7534     
7535     /**
7536      * Returns the Element for this toolbar.
7537      * @return {Roo.Element}
7538      */
7539     getEl : function(){
7540         return this.el;  
7541     },
7542     
7543     /**
7544      * Adds a separator
7545      * @return {Roo.Toolbar.Item} The separator item
7546      */
7547     addSeparator : function(){
7548         return this.addItem(new Roo.Toolbar.Separator());
7549     },
7550
7551     /**
7552      * Adds a spacer element
7553      * @return {Roo.Toolbar.Spacer} The spacer item
7554      */
7555     addSpacer : function(){
7556         return this.addItem(new Roo.Toolbar.Spacer());
7557     },
7558
7559     /**
7560      * Adds a fill element that forces subsequent additions to the right side of the toolbar
7561      * @return {Roo.Toolbar.Fill} The fill item
7562      */
7563     addFill : function(){
7564         return this.addItem(new Roo.Toolbar.Fill());
7565     },
7566
7567     /**
7568      * Adds any standard HTML element to the toolbar
7569      * @param {String/HTMLElement/Element} el The element or id of the element to add
7570      * @return {Roo.Toolbar.Item} The element's item
7571      */
7572     addElement : function(el){
7573         return this.addItem(new Roo.Toolbar.Item(el));
7574     },
7575     /**
7576      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
7577      * @type Roo.util.MixedCollection  
7578      */
7579     items : false,
7580      
7581     /**
7582      * Adds any Toolbar.Item or subclass
7583      * @param {Roo.Toolbar.Item} item
7584      * @return {Roo.Toolbar.Item} The item
7585      */
7586     addItem : function(item){
7587         var td = this.nextBlock();
7588         item.render(td);
7589         this.items.add(item);
7590         return item;
7591     },
7592     
7593     /**
7594      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
7595      * @param {Object/Array} config A button config or array of configs
7596      * @return {Roo.Toolbar.Button/Array}
7597      */
7598     addButton : function(config){
7599         if(config instanceof Array){
7600             var buttons = [];
7601             for(var i = 0, len = config.length; i < len; i++) {
7602                 buttons.push(this.addButton(config[i]));
7603             }
7604             return buttons;
7605         }
7606         var b = config;
7607         if(!(config instanceof Roo.Toolbar.Button)){
7608             b = config.split ?
7609                 new Roo.Toolbar.SplitButton(config) :
7610                 new Roo.Toolbar.Button(config);
7611         }
7612         var td = this.nextBlock();
7613         b.render(td);
7614         this.items.add(b);
7615         return b;
7616     },
7617     
7618     /**
7619      * Adds text to the toolbar
7620      * @param {String} text The text to add
7621      * @return {Roo.Toolbar.Item} The element's item
7622      */
7623     addText : function(text){
7624         return this.addItem(new Roo.Toolbar.TextItem(text));
7625     },
7626     
7627     /**
7628      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
7629      * @param {Number} index The index where the item is to be inserted
7630      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
7631      * @return {Roo.Toolbar.Button/Item}
7632      */
7633     insertButton : function(index, item){
7634         if(item instanceof Array){
7635             var buttons = [];
7636             for(var i = 0, len = item.length; i < len; i++) {
7637                buttons.push(this.insertButton(index + i, item[i]));
7638             }
7639             return buttons;
7640         }
7641         if (!(item instanceof Roo.Toolbar.Button)){
7642            item = new Roo.Toolbar.Button(item);
7643         }
7644         var td = document.createElement("td");
7645         this.tr.insertBefore(td, this.tr.childNodes[index]);
7646         item.render(td);
7647         this.items.insert(index, item);
7648         return item;
7649     },
7650     
7651     /**
7652      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
7653      * @param {Object} config
7654      * @return {Roo.Toolbar.Item} The element's item
7655      */
7656     addDom : function(config, returnEl){
7657         var td = this.nextBlock();
7658         Roo.DomHelper.overwrite(td, config);
7659         var ti = new Roo.Toolbar.Item(td.firstChild);
7660         ti.render(td);
7661         this.items.add(ti);
7662         return ti;
7663     },
7664
7665     /**
7666      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
7667      * @type Roo.util.MixedCollection  
7668      */
7669     fields : false,
7670     
7671     /**
7672      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
7673      * Note: the field should not have been rendered yet. For a field that has already been
7674      * rendered, use {@link #addElement}.
7675      * @param {Roo.form.Field} field
7676      * @return {Roo.ToolbarItem}
7677      */
7678      
7679       
7680     addField : function(field) {
7681         if (!this.fields) {
7682             var autoId = 0;
7683             this.fields = new Roo.util.MixedCollection(false, function(o){
7684                 return o.id || ("item" + (++autoId));
7685             });
7686
7687         }
7688         
7689         var td = this.nextBlock();
7690         field.render(td);
7691         var ti = new Roo.Toolbar.Item(td.firstChild);
7692         ti.render(td);
7693         this.items.add(ti);
7694         this.fields.add(field);
7695         return ti;
7696     },
7697     /**
7698      * Hide the toolbar
7699      * @method hide
7700      */
7701      
7702       
7703     hide : function()
7704     {
7705         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
7706         this.el.child('div').hide();
7707     },
7708     /**
7709      * Show the toolbar
7710      * @method show
7711      */
7712     show : function()
7713     {
7714         this.el.child('div').show();
7715     },
7716       
7717     // private
7718     nextBlock : function(){
7719         var td = document.createElement("td");
7720         this.tr.appendChild(td);
7721         return td;
7722     },
7723
7724     // private
7725     destroy : function(){
7726         if(this.items){ // rendered?
7727             Roo.destroy.apply(Roo, this.items.items);
7728         }
7729         if(this.fields){ // rendered?
7730             Roo.destroy.apply(Roo, this.fields.items);
7731         }
7732         Roo.Element.uncache(this.el, this.tr);
7733     }
7734 };
7735
7736 /**
7737  * @class Roo.Toolbar.Item
7738  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
7739  * @constructor
7740  * Creates a new Item
7741  * @param {HTMLElement} el 
7742  */
7743 Roo.Toolbar.Item = function(el){
7744     var cfg = {};
7745     if (typeof (el.xtype) != 'undefined') {
7746         cfg = el;
7747         el = cfg.el;
7748     }
7749     
7750     this.el = Roo.getDom(el);
7751     this.id = Roo.id(this.el);
7752     this.hidden = false;
7753     
7754     this.addEvents({
7755          /**
7756              * @event render
7757              * Fires when the button is rendered
7758              * @param {Button} this
7759              */
7760         'render': true
7761     });
7762     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
7763 };
7764 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
7765 //Roo.Toolbar.Item.prototype = {
7766     
7767     /**
7768      * Get this item's HTML Element
7769      * @return {HTMLElement}
7770      */
7771     getEl : function(){
7772        return this.el;  
7773     },
7774
7775     // private
7776     render : function(td){
7777         
7778          this.td = td;
7779         td.appendChild(this.el);
7780         
7781         this.fireEvent('render', this);
7782     },
7783     
7784     /**
7785      * Removes and destroys this item.
7786      */
7787     destroy : function(){
7788         this.td.parentNode.removeChild(this.td);
7789     },
7790     
7791     /**
7792      * Shows this item.
7793      */
7794     show: function(){
7795         this.hidden = false;
7796         this.td.style.display = "";
7797     },
7798     
7799     /**
7800      * Hides this item.
7801      */
7802     hide: function(){
7803         this.hidden = true;
7804         this.td.style.display = "none";
7805     },
7806     
7807     /**
7808      * Convenience function for boolean show/hide.
7809      * @param {Boolean} visible true to show/false to hide
7810      */
7811     setVisible: function(visible){
7812         if(visible) {
7813             this.show();
7814         }else{
7815             this.hide();
7816         }
7817     },
7818     
7819     /**
7820      * Try to focus this item.
7821      */
7822     focus : function(){
7823         Roo.fly(this.el).focus();
7824     },
7825     
7826     /**
7827      * Disables this item.
7828      */
7829     disable : function(){
7830         Roo.fly(this.td).addClass("x-item-disabled");
7831         this.disabled = true;
7832         this.el.disabled = true;
7833     },
7834     
7835     /**
7836      * Enables this item.
7837      */
7838     enable : function(){
7839         Roo.fly(this.td).removeClass("x-item-disabled");
7840         this.disabled = false;
7841         this.el.disabled = false;
7842     }
7843 });
7844
7845
7846 /**
7847  * @class Roo.Toolbar.Separator
7848  * @extends Roo.Toolbar.Item
7849  * A simple toolbar separator class
7850  * @constructor
7851  * Creates a new Separator
7852  */
7853 Roo.Toolbar.Separator = function(cfg){
7854     
7855     var s = document.createElement("span");
7856     s.className = "ytb-sep";
7857     if (cfg) {
7858         cfg.el = s;
7859     }
7860     
7861     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
7862 };
7863 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
7864     enable:Roo.emptyFn,
7865     disable:Roo.emptyFn,
7866     focus:Roo.emptyFn
7867 });
7868
7869 /**
7870  * @class Roo.Toolbar.Spacer
7871  * @extends Roo.Toolbar.Item
7872  * A simple element that adds extra horizontal space to a toolbar.
7873  * @constructor
7874  * Creates a new Spacer
7875  */
7876 Roo.Toolbar.Spacer = function(cfg){
7877     var s = document.createElement("div");
7878     s.className = "ytb-spacer";
7879     if (cfg) {
7880         cfg.el = s;
7881     }
7882     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
7883 };
7884 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
7885     enable:Roo.emptyFn,
7886     disable:Roo.emptyFn,
7887     focus:Roo.emptyFn
7888 });
7889
7890 /**
7891  * @class Roo.Toolbar.Fill
7892  * @extends Roo.Toolbar.Spacer
7893  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
7894  * @constructor
7895  * Creates a new Spacer
7896  */
7897 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
7898     // private
7899     render : function(td){
7900         td.style.width = '100%';
7901         Roo.Toolbar.Fill.superclass.render.call(this, td);
7902     }
7903 });
7904
7905 /**
7906  * @class Roo.Toolbar.TextItem
7907  * @extends Roo.Toolbar.Item
7908  * A simple class that renders text directly into a toolbar.
7909  * @constructor
7910  * Creates a new TextItem
7911  * @param {String} text
7912  */
7913 Roo.Toolbar.TextItem = function(cfg){
7914     var  text = cfg || "";
7915     if (typeof(cfg) == 'object') {
7916         text = cfg.text || "";
7917     }  else {
7918         cfg = null;
7919     }
7920     var s = document.createElement("span");
7921     s.className = "ytb-text";
7922     s.innerHTML = text;
7923     if (cfg) {
7924         cfg.el  = s;
7925     }
7926     
7927     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
7928 };
7929 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
7930     
7931      
7932     enable:Roo.emptyFn,
7933     disable:Roo.emptyFn,
7934     focus:Roo.emptyFn
7935 });
7936
7937 /**
7938  * @class Roo.Toolbar.Button
7939  * @extends Roo.Button
7940  * A button that renders into a toolbar.
7941  * @constructor
7942  * Creates a new Button
7943  * @param {Object} config A standard {@link Roo.Button} config object
7944  */
7945 Roo.Toolbar.Button = function(config){
7946     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
7947 };
7948 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
7949     render : function(td){
7950         this.td = td;
7951         Roo.Toolbar.Button.superclass.render.call(this, td);
7952     },
7953     
7954     /**
7955      * Removes and destroys this button
7956      */
7957     destroy : function(){
7958         Roo.Toolbar.Button.superclass.destroy.call(this);
7959         this.td.parentNode.removeChild(this.td);
7960     },
7961     
7962     /**
7963      * Shows this button
7964      */
7965     show: function(){
7966         this.hidden = false;
7967         this.td.style.display = "";
7968     },
7969     
7970     /**
7971      * Hides this button
7972      */
7973     hide: function(){
7974         this.hidden = true;
7975         this.td.style.display = "none";
7976     },
7977
7978     /**
7979      * Disables this item
7980      */
7981     disable : function(){
7982         Roo.fly(this.td).addClass("x-item-disabled");
7983         this.disabled = true;
7984     },
7985
7986     /**
7987      * Enables this item
7988      */
7989     enable : function(){
7990         Roo.fly(this.td).removeClass("x-item-disabled");
7991         this.disabled = false;
7992     }
7993 });
7994 // backwards compat
7995 Roo.ToolbarButton = Roo.Toolbar.Button;
7996
7997 /**
7998  * @class Roo.Toolbar.SplitButton
7999  * @extends Roo.SplitButton
8000  * A menu button that renders into a toolbar.
8001  * @constructor
8002  * Creates a new SplitButton
8003  * @param {Object} config A standard {@link Roo.SplitButton} config object
8004  */
8005 Roo.Toolbar.SplitButton = function(config){
8006     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
8007 };
8008 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
8009     render : function(td){
8010         this.td = td;
8011         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
8012     },
8013     
8014     /**
8015      * Removes and destroys this button
8016      */
8017     destroy : function(){
8018         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
8019         this.td.parentNode.removeChild(this.td);
8020     },
8021     
8022     /**
8023      * Shows this button
8024      */
8025     show: function(){
8026         this.hidden = false;
8027         this.td.style.display = "";
8028     },
8029     
8030     /**
8031      * Hides this button
8032      */
8033     hide: function(){
8034         this.hidden = true;
8035         this.td.style.display = "none";
8036     }
8037 });
8038
8039 // backwards compat
8040 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
8041  * Based on:
8042  * Ext JS Library 1.1.1
8043  * Copyright(c) 2006-2007, Ext JS, LLC.
8044  *
8045  * Originally Released Under LGPL - original licence link has changed is not relivant.
8046  *
8047  * Fork - LGPL
8048  * <script type="text/javascript">
8049  */
8050  
8051 /**
8052  * @class Roo.PagingToolbar
8053  * @extends Roo.Toolbar
8054  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
8055  * @constructor
8056  * Create a new PagingToolbar
8057  * @param {Object} config The config object
8058  */
8059 Roo.PagingToolbar = function(el, ds, config)
8060 {
8061     // old args format still supported... - xtype is prefered..
8062     if (typeof(el) == 'object' && el.xtype) {
8063         // created from xtype...
8064         config = el;
8065         ds = el.dataSource;
8066         el = config.container;
8067     }
8068     var items = [];
8069     if (config.items) {
8070         items = config.items;
8071         config.items = [];
8072     }
8073     
8074     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
8075     this.ds = ds;
8076     this.cursor = 0;
8077     this.renderButtons(this.el);
8078     this.bind(ds);
8079     
8080     // supprot items array.
8081    
8082     Roo.each(items, function(e) {
8083         this.add(Roo.factory(e));
8084     },this);
8085     
8086 };
8087
8088 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
8089     /**
8090      * @cfg {Roo.data.Store} dataSource
8091      * The underlying data store providing the paged data
8092      */
8093     /**
8094      * @cfg {String/HTMLElement/Element} container
8095      * container The id or element that will contain the toolbar
8096      */
8097     /**
8098      * @cfg {Boolean} displayInfo
8099      * True to display the displayMsg (defaults to false)
8100      */
8101     /**
8102      * @cfg {Number} pageSize
8103      * The number of records to display per page (defaults to 20)
8104      */
8105     pageSize: 20,
8106     /**
8107      * @cfg {String} displayMsg
8108      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
8109      */
8110     displayMsg : 'Displaying {0} - {1} of {2}',
8111     /**
8112      * @cfg {String} emptyMsg
8113      * The message to display when no records are found (defaults to "No data to display")
8114      */
8115     emptyMsg : 'No data to display',
8116     /**
8117      * Customizable piece of the default paging text (defaults to "Page")
8118      * @type String
8119      */
8120     beforePageText : "Page",
8121     /**
8122      * Customizable piece of the default paging text (defaults to "of %0")
8123      * @type String
8124      */
8125     afterPageText : "of {0}",
8126     /**
8127      * Customizable piece of the default paging text (defaults to "First Page")
8128      * @type String
8129      */
8130     firstText : "First Page",
8131     /**
8132      * Customizable piece of the default paging text (defaults to "Previous Page")
8133      * @type String
8134      */
8135     prevText : "Previous Page",
8136     /**
8137      * Customizable piece of the default paging text (defaults to "Next Page")
8138      * @type String
8139      */
8140     nextText : "Next Page",
8141     /**
8142      * Customizable piece of the default paging text (defaults to "Last Page")
8143      * @type String
8144      */
8145     lastText : "Last Page",
8146     /**
8147      * Customizable piece of the default paging text (defaults to "Refresh")
8148      * @type String
8149      */
8150     refreshText : "Refresh",
8151
8152     // private
8153     renderButtons : function(el){
8154         Roo.PagingToolbar.superclass.render.call(this, el);
8155         this.first = this.addButton({
8156             tooltip: this.firstText,
8157             cls: "x-btn-icon x-grid-page-first",
8158             disabled: true,
8159             handler: this.onClick.createDelegate(this, ["first"])
8160         });
8161         this.prev = this.addButton({
8162             tooltip: this.prevText,
8163             cls: "x-btn-icon x-grid-page-prev",
8164             disabled: true,
8165             handler: this.onClick.createDelegate(this, ["prev"])
8166         });
8167         //this.addSeparator();
8168         this.add(this.beforePageText);
8169         this.field = Roo.get(this.addDom({
8170            tag: "input",
8171            type: "text",
8172            size: "3",
8173            value: "1",
8174            cls: "x-grid-page-number"
8175         }).el);
8176         this.field.on("keydown", this.onPagingKeydown, this);
8177         this.field.on("focus", function(){this.dom.select();});
8178         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
8179         this.field.setHeight(18);
8180         //this.addSeparator();
8181         this.next = this.addButton({
8182             tooltip: this.nextText,
8183             cls: "x-btn-icon x-grid-page-next",
8184             disabled: true,
8185             handler: this.onClick.createDelegate(this, ["next"])
8186         });
8187         this.last = this.addButton({
8188             tooltip: this.lastText,
8189             cls: "x-btn-icon x-grid-page-last",
8190             disabled: true,
8191             handler: this.onClick.createDelegate(this, ["last"])
8192         });
8193         //this.addSeparator();
8194         this.loading = this.addButton({
8195             tooltip: this.refreshText,
8196             cls: "x-btn-icon x-grid-loading",
8197             handler: this.onClick.createDelegate(this, ["refresh"])
8198         });
8199
8200         if(this.displayInfo){
8201             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
8202         }
8203     },
8204
8205     // private
8206     updateInfo : function(){
8207         if(this.displayEl){
8208             var count = this.ds.getCount();
8209             var msg = count == 0 ?
8210                 this.emptyMsg :
8211                 String.format(
8212                     this.displayMsg,
8213                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
8214                 );
8215             this.displayEl.update(msg);
8216         }
8217     },
8218
8219     // private
8220     onLoad : function(ds, r, o){
8221        this.cursor = o.params ? o.params.start : 0;
8222        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
8223
8224        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
8225        this.field.dom.value = ap;
8226        this.first.setDisabled(ap == 1);
8227        this.prev.setDisabled(ap == 1);
8228        this.next.setDisabled(ap == ps);
8229        this.last.setDisabled(ap == ps);
8230        this.loading.enable();
8231        this.updateInfo();
8232     },
8233
8234     // private
8235     getPageData : function(){
8236         var total = this.ds.getTotalCount();
8237         return {
8238             total : total,
8239             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
8240             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
8241         };
8242     },
8243
8244     // private
8245     onLoadError : function(){
8246         this.loading.enable();
8247     },
8248
8249     // private
8250     onPagingKeydown : function(e){
8251         var k = e.getKey();
8252         var d = this.getPageData();
8253         if(k == e.RETURN){
8254             var v = this.field.dom.value, pageNum;
8255             if(!v || isNaN(pageNum = parseInt(v, 10))){
8256                 this.field.dom.value = d.activePage;
8257                 return;
8258             }
8259             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
8260             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
8261             e.stopEvent();
8262         }
8263         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))
8264         {
8265           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
8266           this.field.dom.value = pageNum;
8267           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
8268           e.stopEvent();
8269         }
8270         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
8271         {
8272           var v = this.field.dom.value, pageNum; 
8273           var increment = (e.shiftKey) ? 10 : 1;
8274           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
8275             increment *= -1;
8276           }
8277           if(!v || isNaN(pageNum = parseInt(v, 10))) {
8278             this.field.dom.value = d.activePage;
8279             return;
8280           }
8281           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
8282           {
8283             this.field.dom.value = parseInt(v, 10) + increment;
8284             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
8285             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
8286           }
8287           e.stopEvent();
8288         }
8289     },
8290
8291     // private
8292     beforeLoad : function(){
8293         if(this.loading){
8294             this.loading.disable();
8295         }
8296     },
8297
8298     // private
8299     onClick : function(which){
8300         var ds = this.ds;
8301         switch(which){
8302             case "first":
8303                 ds.load({params:{start: 0, limit: this.pageSize}});
8304             break;
8305             case "prev":
8306                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
8307             break;
8308             case "next":
8309                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
8310             break;
8311             case "last":
8312                 var total = ds.getTotalCount();
8313                 var extra = total % this.pageSize;
8314                 var lastStart = extra ? (total - extra) : total-this.pageSize;
8315                 ds.load({params:{start: lastStart, limit: this.pageSize}});
8316             break;
8317             case "refresh":
8318                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
8319             break;
8320         }
8321     },
8322
8323     /**
8324      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
8325      * @param {Roo.data.Store} store The data store to unbind
8326      */
8327     unbind : function(ds){
8328         ds.un("beforeload", this.beforeLoad, this);
8329         ds.un("load", this.onLoad, this);
8330         ds.un("loadexception", this.onLoadError, this);
8331         ds.un("remove", this.updateInfo, this);
8332         ds.un("add", this.updateInfo, this);
8333         this.ds = undefined;
8334     },
8335
8336     /**
8337      * Binds the paging toolbar to the specified {@link Roo.data.Store}
8338      * @param {Roo.data.Store} store The data store to bind
8339      */
8340     bind : function(ds){
8341         ds.on("beforeload", this.beforeLoad, this);
8342         ds.on("load", this.onLoad, this);
8343         ds.on("loadexception", this.onLoadError, this);
8344         ds.on("remove", this.updateInfo, this);
8345         ds.on("add", this.updateInfo, this);
8346         this.ds = ds;
8347     }
8348 });/*
8349  * Based on:
8350  * Ext JS Library 1.1.1
8351  * Copyright(c) 2006-2007, Ext JS, LLC.
8352  *
8353  * Originally Released Under LGPL - original licence link has changed is not relivant.
8354  *
8355  * Fork - LGPL
8356  * <script type="text/javascript">
8357  */
8358
8359 /**
8360  * @class Roo.Resizable
8361  * @extends Roo.util.Observable
8362  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
8363  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
8364  * 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
8365  * the element will be wrapped for you automatically.</p>
8366  * <p>Here is the list of valid resize handles:</p>
8367  * <pre>
8368 Value   Description
8369 ------  -------------------
8370  'n'     north
8371  's'     south
8372  'e'     east
8373  'w'     west
8374  'nw'    northwest
8375  'sw'    southwest
8376  'se'    southeast
8377  'ne'    northeast
8378  'hd'    horizontal drag
8379  'all'   all
8380 </pre>
8381  * <p>Here's an example showing the creation of a typical Resizable:</p>
8382  * <pre><code>
8383 var resizer = new Roo.Resizable("element-id", {
8384     handles: 'all',
8385     minWidth: 200,
8386     minHeight: 100,
8387     maxWidth: 500,
8388     maxHeight: 400,
8389     pinned: true
8390 });
8391 resizer.on("resize", myHandler);
8392 </code></pre>
8393  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
8394  * resizer.east.setDisplayed(false);</p>
8395  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
8396  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
8397  * resize operation's new size (defaults to [0, 0])
8398  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
8399  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
8400  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
8401  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
8402  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
8403  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
8404  * @cfg {Number} width The width of the element in pixels (defaults to null)
8405  * @cfg {Number} height The height of the element in pixels (defaults to null)
8406  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
8407  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
8408  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
8409  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
8410  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
8411  * in favor of the handles config option (defaults to false)
8412  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
8413  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
8414  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
8415  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
8416  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
8417  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
8418  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
8419  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
8420  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
8421  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
8422  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
8423  * @constructor
8424  * Create a new resizable component
8425  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
8426  * @param {Object} config configuration options
8427   */
8428 Roo.Resizable = function(el, config)
8429 {
8430     this.el = Roo.get(el);
8431
8432     if(config && config.wrap){
8433         config.resizeChild = this.el;
8434         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
8435         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
8436         this.el.setStyle("overflow", "hidden");
8437         this.el.setPositioning(config.resizeChild.getPositioning());
8438         config.resizeChild.clearPositioning();
8439         if(!config.width || !config.height){
8440             var csize = config.resizeChild.getSize();
8441             this.el.setSize(csize.width, csize.height);
8442         }
8443         if(config.pinned && !config.adjustments){
8444             config.adjustments = "auto";
8445         }
8446     }
8447
8448     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
8449     this.proxy.unselectable();
8450     this.proxy.enableDisplayMode('block');
8451
8452     Roo.apply(this, config);
8453
8454     if(this.pinned){
8455         this.disableTrackOver = true;
8456         this.el.addClass("x-resizable-pinned");
8457     }
8458     // if the element isn't positioned, make it relative
8459     var position = this.el.getStyle("position");
8460     if(position != "absolute" && position != "fixed"){
8461         this.el.setStyle("position", "relative");
8462     }
8463     if(!this.handles){ // no handles passed, must be legacy style
8464         this.handles = 's,e,se';
8465         if(this.multiDirectional){
8466             this.handles += ',n,w';
8467         }
8468     }
8469     if(this.handles == "all"){
8470         this.handles = "n s e w ne nw se sw";
8471     }
8472     var hs = this.handles.split(/\s*?[,;]\s*?| /);
8473     var ps = Roo.Resizable.positions;
8474     for(var i = 0, len = hs.length; i < len; i++){
8475         if(hs[i] && ps[hs[i]]){
8476             var pos = ps[hs[i]];
8477             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
8478         }
8479     }
8480     // legacy
8481     this.corner = this.southeast;
8482     
8483     // updateBox = the box can move..
8484     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
8485         this.updateBox = true;
8486     }
8487
8488     this.activeHandle = null;
8489
8490     if(this.resizeChild){
8491         if(typeof this.resizeChild == "boolean"){
8492             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
8493         }else{
8494             this.resizeChild = Roo.get(this.resizeChild, true);
8495         }
8496     }
8497     
8498     if(this.adjustments == "auto"){
8499         var rc = this.resizeChild;
8500         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
8501         if(rc && (hw || hn)){
8502             rc.position("relative");
8503             rc.setLeft(hw ? hw.el.getWidth() : 0);
8504             rc.setTop(hn ? hn.el.getHeight() : 0);
8505         }
8506         this.adjustments = [
8507             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
8508             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
8509         ];
8510     }
8511
8512     if(this.draggable){
8513         this.dd = this.dynamic ?
8514             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
8515         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
8516     }
8517
8518     // public events
8519     this.addEvents({
8520         /**
8521          * @event beforeresize
8522          * Fired before resize is allowed. Set enabled to false to cancel resize.
8523          * @param {Roo.Resizable} this
8524          * @param {Roo.EventObject} e The mousedown event
8525          */
8526         "beforeresize" : true,
8527         /**
8528          * @event resizing
8529          * Fired a resizing.
8530          * @param {Roo.Resizable} this
8531          * @param {Number} x The new x position
8532          * @param {Number} y The new y position
8533          * @param {Number} w The new w width
8534          * @param {Number} h The new h hight
8535          * @param {Roo.EventObject} e The mouseup event
8536          */
8537         "resizing" : true,
8538         /**
8539          * @event resize
8540          * Fired after a resize.
8541          * @param {Roo.Resizable} this
8542          * @param {Number} width The new width
8543          * @param {Number} height The new height
8544          * @param {Roo.EventObject} e The mouseup event
8545          */
8546         "resize" : true
8547     });
8548
8549     if(this.width !== null && this.height !== null){
8550         this.resizeTo(this.width, this.height);
8551     }else{
8552         this.updateChildSize();
8553     }
8554     if(Roo.isIE){
8555         this.el.dom.style.zoom = 1;
8556     }
8557     Roo.Resizable.superclass.constructor.call(this);
8558 };
8559
8560 Roo.extend(Roo.Resizable, Roo.util.Observable, {
8561         resizeChild : false,
8562         adjustments : [0, 0],
8563         minWidth : 5,
8564         minHeight : 5,
8565         maxWidth : 10000,
8566         maxHeight : 10000,
8567         enabled : true,
8568         animate : false,
8569         duration : .35,
8570         dynamic : false,
8571         handles : false,
8572         multiDirectional : false,
8573         disableTrackOver : false,
8574         easing : 'easeOutStrong',
8575         widthIncrement : 0,
8576         heightIncrement : 0,
8577         pinned : false,
8578         width : null,
8579         height : null,
8580         preserveRatio : false,
8581         transparent: false,
8582         minX: 0,
8583         minY: 0,
8584         draggable: false,
8585
8586         /**
8587          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
8588          */
8589         constrainTo: undefined,
8590         /**
8591          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
8592          */
8593         resizeRegion: undefined,
8594
8595
8596     /**
8597      * Perform a manual resize
8598      * @param {Number} width
8599      * @param {Number} height
8600      */
8601     resizeTo : function(width, height){
8602         this.el.setSize(width, height);
8603         this.updateChildSize();
8604         this.fireEvent("resize", this, width, height, null);
8605     },
8606
8607     // private
8608     startSizing : function(e, handle){
8609         this.fireEvent("beforeresize", this, e);
8610         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
8611
8612             if(!this.overlay){
8613                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
8614                 this.overlay.unselectable();
8615                 this.overlay.enableDisplayMode("block");
8616                 this.overlay.on("mousemove", this.onMouseMove, this);
8617                 this.overlay.on("mouseup", this.onMouseUp, this);
8618             }
8619             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
8620
8621             this.resizing = true;
8622             this.startBox = this.el.getBox();
8623             this.startPoint = e.getXY();
8624             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
8625                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
8626
8627             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8628             this.overlay.show();
8629
8630             if(this.constrainTo) {
8631                 var ct = Roo.get(this.constrainTo);
8632                 this.resizeRegion = ct.getRegion().adjust(
8633                     ct.getFrameWidth('t'),
8634                     ct.getFrameWidth('l'),
8635                     -ct.getFrameWidth('b'),
8636                     -ct.getFrameWidth('r')
8637                 );
8638             }
8639
8640             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
8641             this.proxy.show();
8642             this.proxy.setBox(this.startBox);
8643             if(!this.dynamic){
8644                 this.proxy.setStyle('visibility', 'visible');
8645             }
8646         }
8647     },
8648
8649     // private
8650     onMouseDown : function(handle, e){
8651         if(this.enabled){
8652             e.stopEvent();
8653             this.activeHandle = handle;
8654             this.startSizing(e, handle);
8655         }
8656     },
8657
8658     // private
8659     onMouseUp : function(e){
8660         var size = this.resizeElement();
8661         this.resizing = false;
8662         this.handleOut();
8663         this.overlay.hide();
8664         this.proxy.hide();
8665         this.fireEvent("resize", this, size.width, size.height, e);
8666     },
8667
8668     // private
8669     updateChildSize : function(){
8670         
8671         if(this.resizeChild){
8672             var el = this.el;
8673             var child = this.resizeChild;
8674             var adj = this.adjustments;
8675             if(el.dom.offsetWidth){
8676                 var b = el.getSize(true);
8677                 child.setSize(b.width+adj[0], b.height+adj[1]);
8678             }
8679             // Second call here for IE
8680             // The first call enables instant resizing and
8681             // the second call corrects scroll bars if they
8682             // exist
8683             if(Roo.isIE){
8684                 setTimeout(function(){
8685                     if(el.dom.offsetWidth){
8686                         var b = el.getSize(true);
8687                         child.setSize(b.width+adj[0], b.height+adj[1]);
8688                     }
8689                 }, 10);
8690             }
8691         }
8692     },
8693
8694     // private
8695     snap : function(value, inc, min){
8696         if(!inc || !value) {
8697             return value;
8698         }
8699         var newValue = value;
8700         var m = value % inc;
8701         if(m > 0){
8702             if(m > (inc/2)){
8703                 newValue = value + (inc-m);
8704             }else{
8705                 newValue = value - m;
8706             }
8707         }
8708         return Math.max(min, newValue);
8709     },
8710
8711     // private
8712     resizeElement : function(){
8713         var box = this.proxy.getBox();
8714         if(this.updateBox){
8715             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
8716         }else{
8717             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
8718         }
8719         this.updateChildSize();
8720         if(!this.dynamic){
8721             this.proxy.hide();
8722         }
8723         return box;
8724     },
8725
8726     // private
8727     constrain : function(v, diff, m, mx){
8728         if(v - diff < m){
8729             diff = v - m;
8730         }else if(v - diff > mx){
8731             diff = mx - v;
8732         }
8733         return diff;
8734     },
8735
8736     // private
8737     onMouseMove : function(e){
8738         
8739         if(this.enabled){
8740             try{// try catch so if something goes wrong the user doesn't get hung
8741
8742             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
8743                 return;
8744             }
8745
8746             //var curXY = this.startPoint;
8747             var curSize = this.curSize || this.startBox;
8748             var x = this.startBox.x, y = this.startBox.y;
8749             var ox = x, oy = y;
8750             var w = curSize.width, h = curSize.height;
8751             var ow = w, oh = h;
8752             var mw = this.minWidth, mh = this.minHeight;
8753             var mxw = this.maxWidth, mxh = this.maxHeight;
8754             var wi = this.widthIncrement;
8755             var hi = this.heightIncrement;
8756
8757             var eventXY = e.getXY();
8758             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
8759             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
8760
8761             var pos = this.activeHandle.position;
8762
8763             switch(pos){
8764                 case "east":
8765                     w += diffX;
8766                     w = Math.min(Math.max(mw, w), mxw);
8767                     break;
8768              
8769                 case "south":
8770                     h += diffY;
8771                     h = Math.min(Math.max(mh, h), mxh);
8772                     break;
8773                 case "southeast":
8774                     w += diffX;
8775                     h += diffY;
8776                     w = Math.min(Math.max(mw, w), mxw);
8777                     h = Math.min(Math.max(mh, h), mxh);
8778                     break;
8779                 case "north":
8780                     diffY = this.constrain(h, diffY, mh, mxh);
8781                     y += diffY;
8782                     h -= diffY;
8783                     break;
8784                 case "hdrag":
8785                     
8786                     if (wi) {
8787                         var adiffX = Math.abs(diffX);
8788                         var sub = (adiffX % wi); // how much 
8789                         if (sub > (wi/2)) { // far enough to snap
8790                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
8791                         } else {
8792                             // remove difference.. 
8793                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
8794                         }
8795                     }
8796                     x += diffX;
8797                     x = Math.max(this.minX, x);
8798                     break;
8799                 case "west":
8800                     diffX = this.constrain(w, diffX, mw, mxw);
8801                     x += diffX;
8802                     w -= diffX;
8803                     break;
8804                 case "northeast":
8805                     w += diffX;
8806                     w = Math.min(Math.max(mw, w), mxw);
8807                     diffY = this.constrain(h, diffY, mh, mxh);
8808                     y += diffY;
8809                     h -= diffY;
8810                     break;
8811                 case "northwest":
8812                     diffX = this.constrain(w, diffX, mw, mxw);
8813                     diffY = this.constrain(h, diffY, mh, mxh);
8814                     y += diffY;
8815                     h -= diffY;
8816                     x += diffX;
8817                     w -= diffX;
8818                     break;
8819                case "southwest":
8820                     diffX = this.constrain(w, diffX, mw, mxw);
8821                     h += diffY;
8822                     h = Math.min(Math.max(mh, h), mxh);
8823                     x += diffX;
8824                     w -= diffX;
8825                     break;
8826             }
8827
8828             var sw = this.snap(w, wi, mw);
8829             var sh = this.snap(h, hi, mh);
8830             if(sw != w || sh != h){
8831                 switch(pos){
8832                     case "northeast":
8833                         y -= sh - h;
8834                     break;
8835                     case "north":
8836                         y -= sh - h;
8837                         break;
8838                     case "southwest":
8839                         x -= sw - w;
8840                     break;
8841                     case "west":
8842                         x -= sw - w;
8843                         break;
8844                     case "northwest":
8845                         x -= sw - w;
8846                         y -= sh - h;
8847                     break;
8848                 }
8849                 w = sw;
8850                 h = sh;
8851             }
8852
8853             if(this.preserveRatio){
8854                 switch(pos){
8855                     case "southeast":
8856                     case "east":
8857                         h = oh * (w/ow);
8858                         h = Math.min(Math.max(mh, h), mxh);
8859                         w = ow * (h/oh);
8860                        break;
8861                     case "south":
8862                         w = ow * (h/oh);
8863                         w = Math.min(Math.max(mw, w), mxw);
8864                         h = oh * (w/ow);
8865                         break;
8866                     case "northeast":
8867                         w = ow * (h/oh);
8868                         w = Math.min(Math.max(mw, w), mxw);
8869                         h = oh * (w/ow);
8870                     break;
8871                     case "north":
8872                         var tw = w;
8873                         w = ow * (h/oh);
8874                         w = Math.min(Math.max(mw, w), mxw);
8875                         h = oh * (w/ow);
8876                         x += (tw - w) / 2;
8877                         break;
8878                     case "southwest":
8879                         h = oh * (w/ow);
8880                         h = Math.min(Math.max(mh, h), mxh);
8881                         var tw = w;
8882                         w = ow * (h/oh);
8883                         x += tw - w;
8884                         break;
8885                     case "west":
8886                         var th = h;
8887                         h = oh * (w/ow);
8888                         h = Math.min(Math.max(mh, h), mxh);
8889                         y += (th - h) / 2;
8890                         var tw = w;
8891                         w = ow * (h/oh);
8892                         x += tw - w;
8893                        break;
8894                     case "northwest":
8895                         var tw = w;
8896                         var th = h;
8897                         h = oh * (w/ow);
8898                         h = Math.min(Math.max(mh, h), mxh);
8899                         w = ow * (h/oh);
8900                         y += th - h;
8901                         x += tw - w;
8902                        break;
8903
8904                 }
8905             }
8906             if (pos == 'hdrag') {
8907                 w = ow;
8908             }
8909             this.proxy.setBounds(x, y, w, h);
8910             if(this.dynamic){
8911                 this.resizeElement();
8912             }
8913             }catch(e){}
8914         }
8915         this.fireEvent("resizing", this, x, y, w, h, e);
8916     },
8917
8918     // private
8919     handleOver : function(){
8920         if(this.enabled){
8921             this.el.addClass("x-resizable-over");
8922         }
8923     },
8924
8925     // private
8926     handleOut : function(){
8927         if(!this.resizing){
8928             this.el.removeClass("x-resizable-over");
8929         }
8930     },
8931
8932     /**
8933      * Returns the element this component is bound to.
8934      * @return {Roo.Element}
8935      */
8936     getEl : function(){
8937         return this.el;
8938     },
8939
8940     /**
8941      * Returns the resizeChild element (or null).
8942      * @return {Roo.Element}
8943      */
8944     getResizeChild : function(){
8945         return this.resizeChild;
8946     },
8947     groupHandler : function()
8948     {
8949         
8950     },
8951     /**
8952      * Destroys this resizable. If the element was wrapped and
8953      * removeEl is not true then the element remains.
8954      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
8955      */
8956     destroy : function(removeEl){
8957         this.proxy.remove();
8958         if(this.overlay){
8959             this.overlay.removeAllListeners();
8960             this.overlay.remove();
8961         }
8962         var ps = Roo.Resizable.positions;
8963         for(var k in ps){
8964             if(typeof ps[k] != "function" && this[ps[k]]){
8965                 var h = this[ps[k]];
8966                 h.el.removeAllListeners();
8967                 h.el.remove();
8968             }
8969         }
8970         if(removeEl){
8971             this.el.update("");
8972             this.el.remove();
8973         }
8974     }
8975 });
8976
8977 // private
8978 // hash to map config positions to true positions
8979 Roo.Resizable.positions = {
8980     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
8981     hd: "hdrag"
8982 };
8983
8984 // private
8985 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
8986     if(!this.tpl){
8987         // only initialize the template if resizable is used
8988         var tpl = Roo.DomHelper.createTemplate(
8989             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
8990         );
8991         tpl.compile();
8992         Roo.Resizable.Handle.prototype.tpl = tpl;
8993     }
8994     this.position = pos;
8995     this.rz = rz;
8996     // show north drag fro topdra
8997     var handlepos = pos == 'hdrag' ? 'north' : pos;
8998     
8999     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
9000     if (pos == 'hdrag') {
9001         this.el.setStyle('cursor', 'pointer');
9002     }
9003     this.el.unselectable();
9004     if(transparent){
9005         this.el.setOpacity(0);
9006     }
9007     this.el.on("mousedown", this.onMouseDown, this);
9008     if(!disableTrackOver){
9009         this.el.on("mouseover", this.onMouseOver, this);
9010         this.el.on("mouseout", this.onMouseOut, this);
9011     }
9012 };
9013
9014 // private
9015 Roo.Resizable.Handle.prototype = {
9016     afterResize : function(rz){
9017         Roo.log('after?');
9018         // do nothing
9019     },
9020     // private
9021     onMouseDown : function(e){
9022         this.rz.onMouseDown(this, e);
9023     },
9024     // private
9025     onMouseOver : function(e){
9026         this.rz.handleOver(this, e);
9027     },
9028     // private
9029     onMouseOut : function(e){
9030         this.rz.handleOut(this, e);
9031     }
9032 };/*
9033  * Based on:
9034  * Ext JS Library 1.1.1
9035  * Copyright(c) 2006-2007, Ext JS, LLC.
9036  *
9037  * Originally Released Under LGPL - original licence link has changed is not relivant.
9038  *
9039  * Fork - LGPL
9040  * <script type="text/javascript">
9041  */
9042
9043 /**
9044  * @class Roo.Editor
9045  * @extends Roo.Component
9046  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
9047  * @constructor
9048  * Create a new Editor
9049  * @param {Roo.form.Field} field The Field object (or descendant)
9050  * @param {Object} config The config object
9051  */
9052 Roo.Editor = function(field, config){
9053     Roo.Editor.superclass.constructor.call(this, config);
9054     this.field = field;
9055     this.addEvents({
9056         /**
9057              * @event beforestartedit
9058              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
9059              * false from the handler of this event.
9060              * @param {Editor} this
9061              * @param {Roo.Element} boundEl The underlying element bound to this editor
9062              * @param {Mixed} value The field value being set
9063              */
9064         "beforestartedit" : true,
9065         /**
9066              * @event startedit
9067              * Fires when this editor is displayed
9068              * @param {Roo.Element} boundEl The underlying element bound to this editor
9069              * @param {Mixed} value The starting field value
9070              */
9071         "startedit" : true,
9072         /**
9073              * @event beforecomplete
9074              * Fires after a change has been made to the field, but before the change is reflected in the underlying
9075              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
9076              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
9077              * event will not fire since no edit actually occurred.
9078              * @param {Editor} this
9079              * @param {Mixed} value The current field value
9080              * @param {Mixed} startValue The original field value
9081              */
9082         "beforecomplete" : true,
9083         /**
9084              * @event complete
9085              * Fires after editing is complete and any changed value has been written to the underlying field.
9086              * @param {Editor} this
9087              * @param {Mixed} value The current field value
9088              * @param {Mixed} startValue The original field value
9089              */
9090         "complete" : true,
9091         /**
9092          * @event specialkey
9093          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
9094          * {@link Roo.EventObject#getKey} to determine which key was pressed.
9095          * @param {Roo.form.Field} this
9096          * @param {Roo.EventObject} e The event object
9097          */
9098         "specialkey" : true
9099     });
9100 };
9101
9102 Roo.extend(Roo.Editor, Roo.Component, {
9103     /**
9104      * @cfg {Boolean/String} autosize
9105      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
9106      * or "height" to adopt the height only (defaults to false)
9107      */
9108     /**
9109      * @cfg {Boolean} revertInvalid
9110      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
9111      * validation fails (defaults to true)
9112      */
9113     /**
9114      * @cfg {Boolean} ignoreNoChange
9115      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
9116      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
9117      * will never be ignored.
9118      */
9119     /**
9120      * @cfg {Boolean} hideEl
9121      * False to keep the bound element visible while the editor is displayed (defaults to true)
9122      */
9123     /**
9124      * @cfg {Mixed} value
9125      * The data value of the underlying field (defaults to "")
9126      */
9127     value : "",
9128     /**
9129      * @cfg {String} alignment
9130      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
9131      */
9132     alignment: "c-c?",
9133     /**
9134      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
9135      * for bottom-right shadow (defaults to "frame")
9136      */
9137     shadow : "frame",
9138     /**
9139      * @cfg {Boolean} constrain True to constrain the editor to the viewport
9140      */
9141     constrain : false,
9142     /**
9143      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
9144      */
9145     completeOnEnter : false,
9146     /**
9147      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
9148      */
9149     cancelOnEsc : false,
9150     /**
9151      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
9152      */
9153     updateEl : false,
9154
9155     // private
9156     onRender : function(ct, position){
9157         this.el = new Roo.Layer({
9158             shadow: this.shadow,
9159             cls: "x-editor",
9160             parentEl : ct,
9161             shim : this.shim,
9162             shadowOffset:4,
9163             id: this.id,
9164             constrain: this.constrain
9165         });
9166         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
9167         if(this.field.msgTarget != 'title'){
9168             this.field.msgTarget = 'qtip';
9169         }
9170         this.field.render(this.el);
9171         if(Roo.isGecko){
9172             this.field.el.dom.setAttribute('autocomplete', 'off');
9173         }
9174         this.field.on("specialkey", this.onSpecialKey, this);
9175         if(this.swallowKeys){
9176             this.field.el.swallowEvent(['keydown','keypress']);
9177         }
9178         this.field.show();
9179         this.field.on("blur", this.onBlur, this);
9180         if(this.field.grow){
9181             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
9182         }
9183     },
9184
9185     onSpecialKey : function(field, e)
9186     {
9187         //Roo.log('editor onSpecialKey');
9188         if(this.completeOnEnter && e.getKey() == e.ENTER){
9189             e.stopEvent();
9190             this.completeEdit();
9191             return;
9192         }
9193         // do not fire special key otherwise it might hide close the editor...
9194         if(e.getKey() == e.ENTER){    
9195             return;
9196         }
9197         if(this.cancelOnEsc && e.getKey() == e.ESC){
9198             this.cancelEdit();
9199             return;
9200         } 
9201         this.fireEvent('specialkey', field, e);
9202     
9203     },
9204
9205     /**
9206      * Starts the editing process and shows the editor.
9207      * @param {String/HTMLElement/Element} el The element to edit
9208      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
9209       * to the innerHTML of el.
9210      */
9211     startEdit : function(el, value){
9212         if(this.editing){
9213             this.completeEdit();
9214         }
9215         this.boundEl = Roo.get(el);
9216         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
9217         if(!this.rendered){
9218             this.render(this.parentEl || document.body);
9219         }
9220         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
9221             return;
9222         }
9223         this.startValue = v;
9224         this.field.setValue(v);
9225         if(this.autoSize){
9226             var sz = this.boundEl.getSize();
9227             switch(this.autoSize){
9228                 case "width":
9229                 this.setSize(sz.width,  "");
9230                 break;
9231                 case "height":
9232                 this.setSize("",  sz.height);
9233                 break;
9234                 default:
9235                 this.setSize(sz.width,  sz.height);
9236             }
9237         }
9238         this.el.alignTo(this.boundEl, this.alignment);
9239         this.editing = true;
9240         if(Roo.QuickTips){
9241             Roo.QuickTips.disable();
9242         }
9243         this.show();
9244     },
9245
9246     /**
9247      * Sets the height and width of this editor.
9248      * @param {Number} width The new width
9249      * @param {Number} height The new height
9250      */
9251     setSize : function(w, h){
9252         this.field.setSize(w, h);
9253         if(this.el){
9254             this.el.sync();
9255         }
9256     },
9257
9258     /**
9259      * Realigns the editor to the bound field based on the current alignment config value.
9260      */
9261     realign : function(){
9262         this.el.alignTo(this.boundEl, this.alignment);
9263     },
9264
9265     /**
9266      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
9267      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
9268      */
9269     completeEdit : function(remainVisible){
9270         if(!this.editing){
9271             return;
9272         }
9273         var v = this.getValue();
9274         if(this.revertInvalid !== false && !this.field.isValid()){
9275             v = this.startValue;
9276             this.cancelEdit(true);
9277         }
9278         if(String(v) === String(this.startValue) && this.ignoreNoChange){
9279             this.editing = false;
9280             this.hide();
9281             return;
9282         }
9283         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
9284             this.editing = false;
9285             if(this.updateEl && this.boundEl){
9286                 this.boundEl.update(v);
9287             }
9288             if(remainVisible !== true){
9289                 this.hide();
9290             }
9291             this.fireEvent("complete", this, v, this.startValue);
9292         }
9293     },
9294
9295     // private
9296     onShow : function(){
9297         this.el.show();
9298         if(this.hideEl !== false){
9299             this.boundEl.hide();
9300         }
9301         this.field.show();
9302         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
9303             this.fixIEFocus = true;
9304             this.deferredFocus.defer(50, this);
9305         }else{
9306             this.field.focus();
9307         }
9308         this.fireEvent("startedit", this.boundEl, this.startValue);
9309     },
9310
9311     deferredFocus : function(){
9312         if(this.editing){
9313             this.field.focus();
9314         }
9315     },
9316
9317     /**
9318      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
9319      * reverted to the original starting value.
9320      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
9321      * cancel (defaults to false)
9322      */
9323     cancelEdit : function(remainVisible){
9324         if(this.editing){
9325             this.setValue(this.startValue);
9326             if(remainVisible !== true){
9327                 this.hide();
9328             }
9329         }
9330     },
9331
9332     // private
9333     onBlur : function(){
9334         if(this.allowBlur !== true && this.editing){
9335             this.completeEdit();
9336         }
9337     },
9338
9339     // private
9340     onHide : function(){
9341         if(this.editing){
9342             this.completeEdit();
9343             return;
9344         }
9345         this.field.blur();
9346         if(this.field.collapse){
9347             this.field.collapse();
9348         }
9349         this.el.hide();
9350         if(this.hideEl !== false){
9351             this.boundEl.show();
9352         }
9353         if(Roo.QuickTips){
9354             Roo.QuickTips.enable();
9355         }
9356     },
9357
9358     /**
9359      * Sets the data value of the editor
9360      * @param {Mixed} value Any valid value supported by the underlying field
9361      */
9362     setValue : function(v){
9363         this.field.setValue(v);
9364     },
9365
9366     /**
9367      * Gets the data value of the editor
9368      * @return {Mixed} The data value
9369      */
9370     getValue : function(){
9371         return this.field.getValue();
9372     }
9373 });/*
9374  * Based on:
9375  * Ext JS Library 1.1.1
9376  * Copyright(c) 2006-2007, Ext JS, LLC.
9377  *
9378  * Originally Released Under LGPL - original licence link has changed is not relivant.
9379  *
9380  * Fork - LGPL
9381  * <script type="text/javascript">
9382  */
9383  
9384 /**
9385  * @class Roo.BasicDialog
9386  * @extends Roo.util.Observable
9387  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
9388  * <pre><code>
9389 var dlg = new Roo.BasicDialog("my-dlg", {
9390     height: 200,
9391     width: 300,
9392     minHeight: 100,
9393     minWidth: 150,
9394     modal: true,
9395     proxyDrag: true,
9396     shadow: true
9397 });
9398 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
9399 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
9400 dlg.addButton('Cancel', dlg.hide, dlg);
9401 dlg.show();
9402 </code></pre>
9403   <b>A Dialog should always be a direct child of the body element.</b>
9404  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
9405  * @cfg {String} title Default text to display in the title bar (defaults to null)
9406  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9407  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9408  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
9409  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
9410  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
9411  * (defaults to null with no animation)
9412  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
9413  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
9414  * property for valid values (defaults to 'all')
9415  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
9416  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
9417  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
9418  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
9419  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
9420  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
9421  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
9422  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
9423  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
9424  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
9425  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
9426  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
9427  * draggable = true (defaults to false)
9428  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
9429  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
9430  * shadow (defaults to false)
9431  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
9432  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
9433  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
9434  * @cfg {Array} buttons Array of buttons
9435  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
9436  * @constructor
9437  * Create a new BasicDialog.
9438  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
9439  * @param {Object} config Configuration options
9440  */
9441 Roo.BasicDialog = function(el, config){
9442     this.el = Roo.get(el);
9443     var dh = Roo.DomHelper;
9444     if(!this.el && config && config.autoCreate){
9445         if(typeof config.autoCreate == "object"){
9446             if(!config.autoCreate.id){
9447                 config.autoCreate.id = el;
9448             }
9449             this.el = dh.append(document.body,
9450                         config.autoCreate, true);
9451         }else{
9452             this.el = dh.append(document.body,
9453                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
9454         }
9455     }
9456     el = this.el;
9457     el.setDisplayed(true);
9458     el.hide = this.hideAction;
9459     this.id = el.id;
9460     el.addClass("x-dlg");
9461
9462     Roo.apply(this, config);
9463
9464     this.proxy = el.createProxy("x-dlg-proxy");
9465     this.proxy.hide = this.hideAction;
9466     this.proxy.setOpacity(.5);
9467     this.proxy.hide();
9468
9469     if(config.width){
9470         el.setWidth(config.width);
9471     }
9472     if(config.height){
9473         el.setHeight(config.height);
9474     }
9475     this.size = el.getSize();
9476     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
9477         this.xy = [config.x,config.y];
9478     }else{
9479         this.xy = el.getCenterXY(true);
9480     }
9481     /** The header element @type Roo.Element */
9482     this.header = el.child("> .x-dlg-hd");
9483     /** The body element @type Roo.Element */
9484     this.body = el.child("> .x-dlg-bd");
9485     /** The footer element @type Roo.Element */
9486     this.footer = el.child("> .x-dlg-ft");
9487
9488     if(!this.header){
9489         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
9490     }
9491     if(!this.body){
9492         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
9493     }
9494
9495     this.header.unselectable();
9496     if(this.title){
9497         this.header.update(this.title);
9498     }
9499     // this element allows the dialog to be focused for keyboard event
9500     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
9501     this.focusEl.swallowEvent("click", true);
9502
9503     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
9504
9505     // wrap the body and footer for special rendering
9506     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
9507     if(this.footer){
9508         this.bwrap.dom.appendChild(this.footer.dom);
9509     }
9510
9511     this.bg = this.el.createChild({
9512         tag: "div", cls:"x-dlg-bg",
9513         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
9514     });
9515     this.centerBg = this.bg.child("div.x-dlg-bg-center");
9516
9517
9518     if(this.autoScroll !== false && !this.autoTabs){
9519         this.body.setStyle("overflow", "auto");
9520     }
9521
9522     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
9523
9524     if(this.closable !== false){
9525         this.el.addClass("x-dlg-closable");
9526         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
9527         this.close.on("click", this.closeClick, this);
9528         this.close.addClassOnOver("x-dlg-close-over");
9529     }
9530     if(this.collapsible !== false){
9531         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
9532         this.collapseBtn.on("click", this.collapseClick, this);
9533         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
9534         this.header.on("dblclick", this.collapseClick, this);
9535     }
9536     if(this.resizable !== false){
9537         this.el.addClass("x-dlg-resizable");
9538         this.resizer = new Roo.Resizable(el, {
9539             minWidth: this.minWidth || 80,
9540             minHeight:this.minHeight || 80,
9541             handles: this.resizeHandles || "all",
9542             pinned: true
9543         });
9544         this.resizer.on("beforeresize", this.beforeResize, this);
9545         this.resizer.on("resize", this.onResize, this);
9546     }
9547     if(this.draggable !== false){
9548         el.addClass("x-dlg-draggable");
9549         if (!this.proxyDrag) {
9550             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
9551         }
9552         else {
9553             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
9554         }
9555         dd.setHandleElId(this.header.id);
9556         dd.endDrag = this.endMove.createDelegate(this);
9557         dd.startDrag = this.startMove.createDelegate(this);
9558         dd.onDrag = this.onDrag.createDelegate(this);
9559         dd.scroll = false;
9560         this.dd = dd;
9561     }
9562     if(this.modal){
9563         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
9564         this.mask.enableDisplayMode("block");
9565         this.mask.hide();
9566         this.el.addClass("x-dlg-modal");
9567     }
9568     if(this.shadow){
9569         this.shadow = new Roo.Shadow({
9570             mode : typeof this.shadow == "string" ? this.shadow : "sides",
9571             offset : this.shadowOffset
9572         });
9573     }else{
9574         this.shadowOffset = 0;
9575     }
9576     if(Roo.useShims && this.shim !== false){
9577         this.shim = this.el.createShim();
9578         this.shim.hide = this.hideAction;
9579         this.shim.hide();
9580     }else{
9581         this.shim = false;
9582     }
9583     if(this.autoTabs){
9584         this.initTabs();
9585     }
9586     if (this.buttons) { 
9587         var bts= this.buttons;
9588         this.buttons = [];
9589         Roo.each(bts, function(b) {
9590             this.addButton(b);
9591         }, this);
9592     }
9593     
9594     
9595     this.addEvents({
9596         /**
9597          * @event keydown
9598          * Fires when a key is pressed
9599          * @param {Roo.BasicDialog} this
9600          * @param {Roo.EventObject} e
9601          */
9602         "keydown" : true,
9603         /**
9604          * @event move
9605          * Fires when this dialog is moved by the user.
9606          * @param {Roo.BasicDialog} this
9607          * @param {Number} x The new page X
9608          * @param {Number} y The new page Y
9609          */
9610         "move" : true,
9611         /**
9612          * @event resize
9613          * Fires when this dialog is resized by the user.
9614          * @param {Roo.BasicDialog} this
9615          * @param {Number} width The new width
9616          * @param {Number} height The new height
9617          */
9618         "resize" : true,
9619         /**
9620          * @event beforehide
9621          * Fires before this dialog is hidden.
9622          * @param {Roo.BasicDialog} this
9623          */
9624         "beforehide" : true,
9625         /**
9626          * @event hide
9627          * Fires when this dialog is hidden.
9628          * @param {Roo.BasicDialog} this
9629          */
9630         "hide" : true,
9631         /**
9632          * @event beforeshow
9633          * Fires before this dialog is shown.
9634          * @param {Roo.BasicDialog} this
9635          */
9636         "beforeshow" : true,
9637         /**
9638          * @event show
9639          * Fires when this dialog is shown.
9640          * @param {Roo.BasicDialog} this
9641          */
9642         "show" : true
9643     });
9644     el.on("keydown", this.onKeyDown, this);
9645     el.on("mousedown", this.toFront, this);
9646     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
9647     this.el.hide();
9648     Roo.DialogManager.register(this);
9649     Roo.BasicDialog.superclass.constructor.call(this);
9650 };
9651
9652 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
9653     shadowOffset: Roo.isIE ? 6 : 5,
9654     minHeight: 80,
9655     minWidth: 200,
9656     minButtonWidth: 75,
9657     defaultButton: null,
9658     buttonAlign: "right",
9659     tabTag: 'div',
9660     firstShow: true,
9661
9662     /**
9663      * Sets the dialog title text
9664      * @param {String} text The title text to display
9665      * @return {Roo.BasicDialog} this
9666      */
9667     setTitle : function(text){
9668         this.header.update(text);
9669         return this;
9670     },
9671
9672     // private
9673     closeClick : function(){
9674         this.hide();
9675     },
9676
9677     // private
9678     collapseClick : function(){
9679         this[this.collapsed ? "expand" : "collapse"]();
9680     },
9681
9682     /**
9683      * Collapses the dialog to its minimized state (only the title bar is visible).
9684      * Equivalent to the user clicking the collapse dialog button.
9685      */
9686     collapse : function(){
9687         if(!this.collapsed){
9688             this.collapsed = true;
9689             this.el.addClass("x-dlg-collapsed");
9690             this.restoreHeight = this.el.getHeight();
9691             this.resizeTo(this.el.getWidth(), this.header.getHeight());
9692         }
9693     },
9694
9695     /**
9696      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
9697      * clicking the expand dialog button.
9698      */
9699     expand : function(){
9700         if(this.collapsed){
9701             this.collapsed = false;
9702             this.el.removeClass("x-dlg-collapsed");
9703             this.resizeTo(this.el.getWidth(), this.restoreHeight);
9704         }
9705     },
9706
9707     /**
9708      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
9709      * @return {Roo.TabPanel} The tabs component
9710      */
9711     initTabs : function(){
9712         var tabs = this.getTabs();
9713         while(tabs.getTab(0)){
9714             tabs.removeTab(0);
9715         }
9716         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
9717             var dom = el.dom;
9718             tabs.addTab(Roo.id(dom), dom.title);
9719             dom.title = "";
9720         });
9721         tabs.activate(0);
9722         return tabs;
9723     },
9724
9725     // private
9726     beforeResize : function(){
9727         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
9728     },
9729
9730     // private
9731     onResize : function(){
9732         this.refreshSize();
9733         this.syncBodyHeight();
9734         this.adjustAssets();
9735         this.focus();
9736         this.fireEvent("resize", this, this.size.width, this.size.height);
9737     },
9738
9739     // private
9740     onKeyDown : function(e){
9741         if(this.isVisible()){
9742             this.fireEvent("keydown", this, e);
9743         }
9744     },
9745
9746     /**
9747      * Resizes the dialog.
9748      * @param {Number} width
9749      * @param {Number} height
9750      * @return {Roo.BasicDialog} this
9751      */
9752     resizeTo : function(width, height){
9753         this.el.setSize(width, height);
9754         this.size = {width: width, height: height};
9755         this.syncBodyHeight();
9756         if(this.fixedcenter){
9757             this.center();
9758         }
9759         if(this.isVisible()){
9760             this.constrainXY();
9761             this.adjustAssets();
9762         }
9763         this.fireEvent("resize", this, width, height);
9764         return this;
9765     },
9766
9767
9768     /**
9769      * Resizes the dialog to fit the specified content size.
9770      * @param {Number} width
9771      * @param {Number} height
9772      * @return {Roo.BasicDialog} this
9773      */
9774     setContentSize : function(w, h){
9775         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
9776         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
9777         //if(!this.el.isBorderBox()){
9778             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
9779             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
9780         //}
9781         if(this.tabs){
9782             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
9783             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
9784         }
9785         this.resizeTo(w, h);
9786         return this;
9787     },
9788
9789     /**
9790      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
9791      * executed in response to a particular key being pressed while the dialog is active.
9792      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
9793      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9794      * @param {Function} fn The function to call
9795      * @param {Object} scope (optional) The scope of the function
9796      * @return {Roo.BasicDialog} this
9797      */
9798     addKeyListener : function(key, fn, scope){
9799         var keyCode, shift, ctrl, alt;
9800         if(typeof key == "object" && !(key instanceof Array)){
9801             keyCode = key["key"];
9802             shift = key["shift"];
9803             ctrl = key["ctrl"];
9804             alt = key["alt"];
9805         }else{
9806             keyCode = key;
9807         }
9808         var handler = function(dlg, e){
9809             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
9810                 var k = e.getKey();
9811                 if(keyCode instanceof Array){
9812                     for(var i = 0, len = keyCode.length; i < len; i++){
9813                         if(keyCode[i] == k){
9814                           fn.call(scope || window, dlg, k, e);
9815                           return;
9816                         }
9817                     }
9818                 }else{
9819                     if(k == keyCode){
9820                         fn.call(scope || window, dlg, k, e);
9821                     }
9822                 }
9823             }
9824         };
9825         this.on("keydown", handler);
9826         return this;
9827     },
9828
9829     /**
9830      * Returns the TabPanel component (creates it if it doesn't exist).
9831      * Note: If you wish to simply check for the existence of tabs without creating them,
9832      * check for a null 'tabs' property.
9833      * @return {Roo.TabPanel} The tabs component
9834      */
9835     getTabs : function(){
9836         if(!this.tabs){
9837             this.el.addClass("x-dlg-auto-tabs");
9838             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
9839             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
9840         }
9841         return this.tabs;
9842     },
9843
9844     /**
9845      * Adds a button to the footer section of the dialog.
9846      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
9847      * object or a valid Roo.DomHelper element config
9848      * @param {Function} handler The function called when the button is clicked
9849      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
9850      * @return {Roo.Button} The new button
9851      */
9852     addButton : function(config, handler, scope){
9853         var dh = Roo.DomHelper;
9854         if(!this.footer){
9855             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
9856         }
9857         if(!this.btnContainer){
9858             var tb = this.footer.createChild({
9859
9860                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
9861                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
9862             }, null, true);
9863             this.btnContainer = tb.firstChild.firstChild.firstChild;
9864         }
9865         var bconfig = {
9866             handler: handler,
9867             scope: scope,
9868             minWidth: this.minButtonWidth,
9869             hideParent:true
9870         };
9871         if(typeof config == "string"){
9872             bconfig.text = config;
9873         }else{
9874             if(config.tag){
9875                 bconfig.dhconfig = config;
9876             }else{
9877                 Roo.apply(bconfig, config);
9878             }
9879         }
9880         var fc = false;
9881         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
9882             bconfig.position = Math.max(0, bconfig.position);
9883             fc = this.btnContainer.childNodes[bconfig.position];
9884         }
9885          
9886         var btn = new Roo.Button(
9887             fc ? 
9888                 this.btnContainer.insertBefore(document.createElement("td"),fc)
9889                 : this.btnContainer.appendChild(document.createElement("td")),
9890             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
9891             bconfig
9892         );
9893         this.syncBodyHeight();
9894         if(!this.buttons){
9895             /**
9896              * Array of all the buttons that have been added to this dialog via addButton
9897              * @type Array
9898              */
9899             this.buttons = [];
9900         }
9901         this.buttons.push(btn);
9902         return btn;
9903     },
9904
9905     /**
9906      * Sets the default button to be focused when the dialog is displayed.
9907      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
9908      * @return {Roo.BasicDialog} this
9909      */
9910     setDefaultButton : function(btn){
9911         this.defaultButton = btn;
9912         return this;
9913     },
9914
9915     // private
9916     getHeaderFooterHeight : function(safe){
9917         var height = 0;
9918         if(this.header){
9919            height += this.header.getHeight();
9920         }
9921         if(this.footer){
9922            var fm = this.footer.getMargins();
9923             height += (this.footer.getHeight()+fm.top+fm.bottom);
9924         }
9925         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
9926         height += this.centerBg.getPadding("tb");
9927         return height;
9928     },
9929
9930     // private
9931     syncBodyHeight : function()
9932     {
9933         var bd = this.body, // the text
9934             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
9935             bw = this.bwrap;
9936         var height = this.size.height - this.getHeaderFooterHeight(false);
9937         bd.setHeight(height-bd.getMargins("tb"));
9938         var hh = this.header.getHeight();
9939         var h = this.size.height-hh;
9940         cb.setHeight(h);
9941         
9942         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
9943         bw.setHeight(h-cb.getPadding("tb"));
9944         
9945         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
9946         bd.setWidth(bw.getWidth(true));
9947         if(this.tabs){
9948             this.tabs.syncHeight();
9949             if(Roo.isIE){
9950                 this.tabs.el.repaint();
9951             }
9952         }
9953     },
9954
9955     /**
9956      * Restores the previous state of the dialog if Roo.state is configured.
9957      * @return {Roo.BasicDialog} this
9958      */
9959     restoreState : function(){
9960         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
9961         if(box && box.width){
9962             this.xy = [box.x, box.y];
9963             this.resizeTo(box.width, box.height);
9964         }
9965         return this;
9966     },
9967
9968     // private
9969     beforeShow : function(){
9970         this.expand();
9971         if(this.fixedcenter){
9972             this.xy = this.el.getCenterXY(true);
9973         }
9974         if(this.modal){
9975             Roo.get(document.body).addClass("x-body-masked");
9976             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
9977             this.mask.show();
9978         }
9979         this.constrainXY();
9980     },
9981
9982     // private
9983     animShow : function(){
9984         var b = Roo.get(this.animateTarget).getBox();
9985         this.proxy.setSize(b.width, b.height);
9986         this.proxy.setLocation(b.x, b.y);
9987         this.proxy.show();
9988         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
9989                     true, .35, this.showEl.createDelegate(this));
9990     },
9991
9992     /**
9993      * Shows the dialog.
9994      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
9995      * @return {Roo.BasicDialog} this
9996      */
9997     show : function(animateTarget){
9998         if (this.fireEvent("beforeshow", this) === false){
9999             return;
10000         }
10001         if(this.syncHeightBeforeShow){
10002             this.syncBodyHeight();
10003         }else if(this.firstShow){
10004             this.firstShow = false;
10005             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
10006         }
10007         this.animateTarget = animateTarget || this.animateTarget;
10008         if(!this.el.isVisible()){
10009             this.beforeShow();
10010             if(this.animateTarget && Roo.get(this.animateTarget)){
10011                 this.animShow();
10012             }else{
10013                 this.showEl();
10014             }
10015         }
10016         return this;
10017     },
10018
10019     // private
10020     showEl : function(){
10021         this.proxy.hide();
10022         this.el.setXY(this.xy);
10023         this.el.show();
10024         this.adjustAssets(true);
10025         this.toFront();
10026         this.focus();
10027         // IE peekaboo bug - fix found by Dave Fenwick
10028         if(Roo.isIE){
10029             this.el.repaint();
10030         }
10031         this.fireEvent("show", this);
10032     },
10033
10034     /**
10035      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
10036      * dialog itself will receive focus.
10037      */
10038     focus : function(){
10039         if(this.defaultButton){
10040             this.defaultButton.focus();
10041         }else{
10042             this.focusEl.focus();
10043         }
10044     },
10045
10046     // private
10047     constrainXY : function(){
10048         if(this.constraintoviewport !== false){
10049             if(!this.viewSize){
10050                 if(this.container){
10051                     var s = this.container.getSize();
10052                     this.viewSize = [s.width, s.height];
10053                 }else{
10054                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
10055                 }
10056             }
10057             var s = Roo.get(this.container||document).getScroll();
10058
10059             var x = this.xy[0], y = this.xy[1];
10060             var w = this.size.width, h = this.size.height;
10061             var vw = this.viewSize[0], vh = this.viewSize[1];
10062             // only move it if it needs it
10063             var moved = false;
10064             // first validate right/bottom
10065             if(x + w > vw+s.left){
10066                 x = vw - w;
10067                 moved = true;
10068             }
10069             if(y + h > vh+s.top){
10070                 y = vh - h;
10071                 moved = true;
10072             }
10073             // then make sure top/left isn't negative
10074             if(x < s.left){
10075                 x = s.left;
10076                 moved = true;
10077             }
10078             if(y < s.top){
10079                 y = s.top;
10080                 moved = true;
10081             }
10082             if(moved){
10083                 // cache xy
10084                 this.xy = [x, y];
10085                 if(this.isVisible()){
10086                     this.el.setLocation(x, y);
10087                     this.adjustAssets();
10088                 }
10089             }
10090         }
10091     },
10092
10093     // private
10094     onDrag : function(){
10095         if(!this.proxyDrag){
10096             this.xy = this.el.getXY();
10097             this.adjustAssets();
10098         }
10099     },
10100
10101     // private
10102     adjustAssets : function(doShow){
10103         var x = this.xy[0], y = this.xy[1];
10104         var w = this.size.width, h = this.size.height;
10105         if(doShow === true){
10106             if(this.shadow){
10107                 this.shadow.show(this.el);
10108             }
10109             if(this.shim){
10110                 this.shim.show();
10111             }
10112         }
10113         if(this.shadow && this.shadow.isVisible()){
10114             this.shadow.show(this.el);
10115         }
10116         if(this.shim && this.shim.isVisible()){
10117             this.shim.setBounds(x, y, w, h);
10118         }
10119     },
10120
10121     // private
10122     adjustViewport : function(w, h){
10123         if(!w || !h){
10124             w = Roo.lib.Dom.getViewWidth();
10125             h = Roo.lib.Dom.getViewHeight();
10126         }
10127         // cache the size
10128         this.viewSize = [w, h];
10129         if(this.modal && this.mask.isVisible()){
10130             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
10131             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
10132         }
10133         if(this.isVisible()){
10134             this.constrainXY();
10135         }
10136     },
10137
10138     /**
10139      * Destroys this dialog and all its supporting elements (including any tabs, shim,
10140      * shadow, proxy, mask, etc.)  Also removes all event listeners.
10141      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
10142      */
10143     destroy : function(removeEl){
10144         if(this.isVisible()){
10145             this.animateTarget = null;
10146             this.hide();
10147         }
10148         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
10149         if(this.tabs){
10150             this.tabs.destroy(removeEl);
10151         }
10152         Roo.destroy(
10153              this.shim,
10154              this.proxy,
10155              this.resizer,
10156              this.close,
10157              this.mask
10158         );
10159         if(this.dd){
10160             this.dd.unreg();
10161         }
10162         if(this.buttons){
10163            for(var i = 0, len = this.buttons.length; i < len; i++){
10164                this.buttons[i].destroy();
10165            }
10166         }
10167         this.el.removeAllListeners();
10168         if(removeEl === true){
10169             this.el.update("");
10170             this.el.remove();
10171         }
10172         Roo.DialogManager.unregister(this);
10173     },
10174
10175     // private
10176     startMove : function(){
10177         if(this.proxyDrag){
10178             this.proxy.show();
10179         }
10180         if(this.constraintoviewport !== false){
10181             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
10182         }
10183     },
10184
10185     // private
10186     endMove : function(){
10187         if(!this.proxyDrag){
10188             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
10189         }else{
10190             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
10191             this.proxy.hide();
10192         }
10193         this.refreshSize();
10194         this.adjustAssets();
10195         this.focus();
10196         this.fireEvent("move", this, this.xy[0], this.xy[1]);
10197     },
10198
10199     /**
10200      * Brings this dialog to the front of any other visible dialogs
10201      * @return {Roo.BasicDialog} this
10202      */
10203     toFront : function(){
10204         Roo.DialogManager.bringToFront(this);
10205         return this;
10206     },
10207
10208     /**
10209      * Sends this dialog to the back (under) of any other visible dialogs
10210      * @return {Roo.BasicDialog} this
10211      */
10212     toBack : function(){
10213         Roo.DialogManager.sendToBack(this);
10214         return this;
10215     },
10216
10217     /**
10218      * Centers this dialog in the viewport
10219      * @return {Roo.BasicDialog} this
10220      */
10221     center : function(){
10222         var xy = this.el.getCenterXY(true);
10223         this.moveTo(xy[0], xy[1]);
10224         return this;
10225     },
10226
10227     /**
10228      * Moves the dialog's top-left corner to the specified point
10229      * @param {Number} x
10230      * @param {Number} y
10231      * @return {Roo.BasicDialog} this
10232      */
10233     moveTo : function(x, y){
10234         this.xy = [x,y];
10235         if(this.isVisible()){
10236             this.el.setXY(this.xy);
10237             this.adjustAssets();
10238         }
10239         return this;
10240     },
10241
10242     /**
10243      * Aligns the dialog to the specified element
10244      * @param {String/HTMLElement/Roo.Element} element The element to align to.
10245      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
10246      * @param {Array} offsets (optional) Offset the positioning by [x, y]
10247      * @return {Roo.BasicDialog} this
10248      */
10249     alignTo : function(element, position, offsets){
10250         this.xy = this.el.getAlignToXY(element, position, offsets);
10251         if(this.isVisible()){
10252             this.el.setXY(this.xy);
10253             this.adjustAssets();
10254         }
10255         return this;
10256     },
10257
10258     /**
10259      * Anchors an element to another element and realigns it when the window is resized.
10260      * @param {String/HTMLElement/Roo.Element} element The element to align to.
10261      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
10262      * @param {Array} offsets (optional) Offset the positioning by [x, y]
10263      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
10264      * is a number, it is used as the buffer delay (defaults to 50ms).
10265      * @return {Roo.BasicDialog} this
10266      */
10267     anchorTo : function(el, alignment, offsets, monitorScroll){
10268         var action = function(){
10269             this.alignTo(el, alignment, offsets);
10270         };
10271         Roo.EventManager.onWindowResize(action, this);
10272         var tm = typeof monitorScroll;
10273         if(tm != 'undefined'){
10274             Roo.EventManager.on(window, 'scroll', action, this,
10275                 {buffer: tm == 'number' ? monitorScroll : 50});
10276         }
10277         action.call(this);
10278         return this;
10279     },
10280
10281     /**
10282      * Returns true if the dialog is visible
10283      * @return {Boolean}
10284      */
10285     isVisible : function(){
10286         return this.el.isVisible();
10287     },
10288
10289     // private
10290     animHide : function(callback){
10291         var b = Roo.get(this.animateTarget).getBox();
10292         this.proxy.show();
10293         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
10294         this.el.hide();
10295         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
10296                     this.hideEl.createDelegate(this, [callback]));
10297     },
10298
10299     /**
10300      * Hides the dialog.
10301      * @param {Function} callback (optional) Function to call when the dialog is hidden
10302      * @return {Roo.BasicDialog} this
10303      */
10304     hide : function(callback){
10305         if (this.fireEvent("beforehide", this) === false){
10306             return;
10307         }
10308         if(this.shadow){
10309             this.shadow.hide();
10310         }
10311         if(this.shim) {
10312           this.shim.hide();
10313         }
10314         // sometimes animateTarget seems to get set.. causing problems...
10315         // this just double checks..
10316         if(this.animateTarget && Roo.get(this.animateTarget)) {
10317            this.animHide(callback);
10318         }else{
10319             this.el.hide();
10320             this.hideEl(callback);
10321         }
10322         return this;
10323     },
10324
10325     // private
10326     hideEl : function(callback){
10327         this.proxy.hide();
10328         if(this.modal){
10329             this.mask.hide();
10330             Roo.get(document.body).removeClass("x-body-masked");
10331         }
10332         this.fireEvent("hide", this);
10333         if(typeof callback == "function"){
10334             callback();
10335         }
10336     },
10337
10338     // private
10339     hideAction : function(){
10340         this.setLeft("-10000px");
10341         this.setTop("-10000px");
10342         this.setStyle("visibility", "hidden");
10343     },
10344
10345     // private
10346     refreshSize : function(){
10347         this.size = this.el.getSize();
10348         this.xy = this.el.getXY();
10349         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
10350     },
10351
10352     // private
10353     // z-index is managed by the DialogManager and may be overwritten at any time
10354     setZIndex : function(index){
10355         if(this.modal){
10356             this.mask.setStyle("z-index", index);
10357         }
10358         if(this.shim){
10359             this.shim.setStyle("z-index", ++index);
10360         }
10361         if(this.shadow){
10362             this.shadow.setZIndex(++index);
10363         }
10364         this.el.setStyle("z-index", ++index);
10365         if(this.proxy){
10366             this.proxy.setStyle("z-index", ++index);
10367         }
10368         if(this.resizer){
10369             this.resizer.proxy.setStyle("z-index", ++index);
10370         }
10371
10372         this.lastZIndex = index;
10373     },
10374
10375     /**
10376      * Returns the element for this dialog
10377      * @return {Roo.Element} The underlying dialog Element
10378      */
10379     getEl : function(){
10380         return this.el;
10381     }
10382 });
10383
10384 /**
10385  * @class Roo.DialogManager
10386  * Provides global access to BasicDialogs that have been created and
10387  * support for z-indexing (layering) multiple open dialogs.
10388  */
10389 Roo.DialogManager = function(){
10390     var list = {};
10391     var accessList = [];
10392     var front = null;
10393
10394     // private
10395     var sortDialogs = function(d1, d2){
10396         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
10397     };
10398
10399     // private
10400     var orderDialogs = function(){
10401         accessList.sort(sortDialogs);
10402         var seed = Roo.DialogManager.zseed;
10403         for(var i = 0, len = accessList.length; i < len; i++){
10404             var dlg = accessList[i];
10405             if(dlg){
10406                 dlg.setZIndex(seed + (i*10));
10407             }
10408         }
10409     };
10410
10411     return {
10412         /**
10413          * The starting z-index for BasicDialogs (defaults to 9000)
10414          * @type Number The z-index value
10415          */
10416         zseed : 9000,
10417
10418         // private
10419         register : function(dlg){
10420             list[dlg.id] = dlg;
10421             accessList.push(dlg);
10422         },
10423
10424         // private
10425         unregister : function(dlg){
10426             delete list[dlg.id];
10427             var i=0;
10428             var len=0;
10429             if(!accessList.indexOf){
10430                 for(  i = 0, len = accessList.length; i < len; i++){
10431                     if(accessList[i] == dlg){
10432                         accessList.splice(i, 1);
10433                         return;
10434                     }
10435                 }
10436             }else{
10437                  i = accessList.indexOf(dlg);
10438                 if(i != -1){
10439                     accessList.splice(i, 1);
10440                 }
10441             }
10442         },
10443
10444         /**
10445          * Gets a registered dialog by id
10446          * @param {String/Object} id The id of the dialog or a dialog
10447          * @return {Roo.BasicDialog} this
10448          */
10449         get : function(id){
10450             return typeof id == "object" ? id : list[id];
10451         },
10452
10453         /**
10454          * Brings the specified dialog to the front
10455          * @param {String/Object} dlg The id of the dialog or a dialog
10456          * @return {Roo.BasicDialog} this
10457          */
10458         bringToFront : function(dlg){
10459             dlg = this.get(dlg);
10460             if(dlg != front){
10461                 front = dlg;
10462                 dlg._lastAccess = new Date().getTime();
10463                 orderDialogs();
10464             }
10465             return dlg;
10466         },
10467
10468         /**
10469          * Sends the specified dialog to the back
10470          * @param {String/Object} dlg The id of the dialog or a dialog
10471          * @return {Roo.BasicDialog} this
10472          */
10473         sendToBack : function(dlg){
10474             dlg = this.get(dlg);
10475             dlg._lastAccess = -(new Date().getTime());
10476             orderDialogs();
10477             return dlg;
10478         },
10479
10480         /**
10481          * Hides all dialogs
10482          */
10483         hideAll : function(){
10484             for(var id in list){
10485                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
10486                     list[id].hide();
10487                 }
10488             }
10489         }
10490     };
10491 }();
10492
10493 /**
10494  * @class Roo.LayoutDialog
10495  * @extends Roo.BasicDialog
10496  * Dialog which provides adjustments for working with a layout in a Dialog.
10497  * Add your necessary layout config options to the dialog's config.<br>
10498  * Example usage (including a nested layout):
10499  * <pre><code>
10500 if(!dialog){
10501     dialog = new Roo.LayoutDialog("download-dlg", {
10502         modal: true,
10503         width:600,
10504         height:450,
10505         shadow:true,
10506         minWidth:500,
10507         minHeight:350,
10508         autoTabs:true,
10509         proxyDrag:true,
10510         // layout config merges with the dialog config
10511         center:{
10512             tabPosition: "top",
10513             alwaysShowTabs: true
10514         }
10515     });
10516     dialog.addKeyListener(27, dialog.hide, dialog);
10517     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
10518     dialog.addButton("Build It!", this.getDownload, this);
10519
10520     // we can even add nested layouts
10521     var innerLayout = new Roo.BorderLayout("dl-inner", {
10522         east: {
10523             initialSize: 200,
10524             autoScroll:true,
10525             split:true
10526         },
10527         center: {
10528             autoScroll:true
10529         }
10530     });
10531     innerLayout.beginUpdate();
10532     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
10533     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
10534     innerLayout.endUpdate(true);
10535
10536     var layout = dialog.getLayout();
10537     layout.beginUpdate();
10538     layout.add("center", new Roo.ContentPanel("standard-panel",
10539                         {title: "Download the Source", fitToFrame:true}));
10540     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
10541                {title: "Build your own roo.js"}));
10542     layout.getRegion("center").showPanel(sp);
10543     layout.endUpdate();
10544 }
10545 </code></pre>
10546     * @constructor
10547     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
10548     * @param {Object} config configuration options
10549   */
10550 Roo.LayoutDialog = function(el, cfg){
10551     
10552     var config=  cfg;
10553     if (typeof(cfg) == 'undefined') {
10554         config = Roo.apply({}, el);
10555         // not sure why we use documentElement here.. - it should always be body.
10556         // IE7 borks horribly if we use documentElement.
10557         // webkit also does not like documentElement - it creates a body element...
10558         el = Roo.get( document.body || document.documentElement ).createChild();
10559         //config.autoCreate = true;
10560     }
10561     
10562     
10563     config.autoTabs = false;
10564     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
10565     this.body.setStyle({overflow:"hidden", position:"relative"});
10566     this.layout = new Roo.BorderLayout(this.body.dom, config);
10567     this.layout.monitorWindowResize = false;
10568     this.el.addClass("x-dlg-auto-layout");
10569     // fix case when center region overwrites center function
10570     this.center = Roo.BasicDialog.prototype.center;
10571     this.on("show", this.layout.layout, this.layout, true);
10572     if (config.items) {
10573         var xitems = config.items;
10574         delete config.items;
10575         Roo.each(xitems, this.addxtype, this);
10576     }
10577     
10578     
10579 };
10580 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
10581     /**
10582      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
10583      * @deprecated
10584      */
10585     endUpdate : function(){
10586         this.layout.endUpdate();
10587     },
10588
10589     /**
10590      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
10591      *  @deprecated
10592      */
10593     beginUpdate : function(){
10594         this.layout.beginUpdate();
10595     },
10596
10597     /**
10598      * Get the BorderLayout for this dialog
10599      * @return {Roo.BorderLayout}
10600      */
10601     getLayout : function(){
10602         return this.layout;
10603     },
10604
10605     showEl : function(){
10606         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
10607         if(Roo.isIE7){
10608             this.layout.layout();
10609         }
10610     },
10611
10612     // private
10613     // Use the syncHeightBeforeShow config option to control this automatically
10614     syncBodyHeight : function(){
10615         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
10616         if(this.layout){this.layout.layout();}
10617     },
10618     
10619       /**
10620      * Add an xtype element (actually adds to the layout.)
10621      * @return {Object} xdata xtype object data.
10622      */
10623     
10624     addxtype : function(c) {
10625         return this.layout.addxtype(c);
10626     }
10627 });/*
10628  * Based on:
10629  * Ext JS Library 1.1.1
10630  * Copyright(c) 2006-2007, Ext JS, LLC.
10631  *
10632  * Originally Released Under LGPL - original licence link has changed is not relivant.
10633  *
10634  * Fork - LGPL
10635  * <script type="text/javascript">
10636  */
10637  
10638 /**
10639  * @class Roo.MessageBox
10640  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
10641  * Example usage:
10642  *<pre><code>
10643 // Basic alert:
10644 Roo.Msg.alert('Status', 'Changes saved successfully.');
10645
10646 // Prompt for user data:
10647 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
10648     if (btn == 'ok'){
10649         // process text value...
10650     }
10651 });
10652
10653 // Show a dialog using config options:
10654 Roo.Msg.show({
10655    title:'Save Changes?',
10656    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
10657    buttons: Roo.Msg.YESNOCANCEL,
10658    fn: processResult,
10659    animEl: 'elId'
10660 });
10661 </code></pre>
10662  * @singleton
10663  */
10664 Roo.MessageBox = function(){
10665     var dlg, opt, mask, waitTimer;
10666     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
10667     var buttons, activeTextEl, bwidth;
10668
10669     // private
10670     var handleButton = function(button){
10671         dlg.hide();
10672         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
10673     };
10674
10675     // private
10676     var handleHide = function(){
10677         if(opt && opt.cls){
10678             dlg.el.removeClass(opt.cls);
10679         }
10680         if(waitTimer){
10681             Roo.TaskMgr.stop(waitTimer);
10682             waitTimer = null;
10683         }
10684     };
10685
10686     // private
10687     var updateButtons = function(b){
10688         var width = 0;
10689         if(!b){
10690             buttons["ok"].hide();
10691             buttons["cancel"].hide();
10692             buttons["yes"].hide();
10693             buttons["no"].hide();
10694             dlg.footer.dom.style.display = 'none';
10695             return width;
10696         }
10697         dlg.footer.dom.style.display = '';
10698         for(var k in buttons){
10699             if(typeof buttons[k] != "function"){
10700                 if(b[k]){
10701                     buttons[k].show();
10702                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
10703                     width += buttons[k].el.getWidth()+15;
10704                 }else{
10705                     buttons[k].hide();
10706                 }
10707             }
10708         }
10709         return width;
10710     };
10711
10712     // private
10713     var handleEsc = function(d, k, e){
10714         if(opt && opt.closable !== false){
10715             dlg.hide();
10716         }
10717         if(e){
10718             e.stopEvent();
10719         }
10720     };
10721
10722     return {
10723         /**
10724          * Returns a reference to the underlying {@link Roo.BasicDialog} element
10725          * @return {Roo.BasicDialog} The BasicDialog element
10726          */
10727         getDialog : function(){
10728            if(!dlg){
10729                 dlg = new Roo.BasicDialog("x-msg-box", {
10730                     autoCreate : true,
10731                     shadow: true,
10732                     draggable: true,
10733                     resizable:false,
10734                     constraintoviewport:false,
10735                     fixedcenter:true,
10736                     collapsible : false,
10737                     shim:true,
10738                     modal: true,
10739                     width:400, height:100,
10740                     buttonAlign:"center",
10741                     closeClick : function(){
10742                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
10743                             handleButton("no");
10744                         }else{
10745                             handleButton("cancel");
10746                         }
10747                     }
10748                 });
10749                 dlg.on("hide", handleHide);
10750                 mask = dlg.mask;
10751                 dlg.addKeyListener(27, handleEsc);
10752                 buttons = {};
10753                 var bt = this.buttonText;
10754                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
10755                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
10756                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
10757                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
10758                 bodyEl = dlg.body.createChild({
10759
10760                     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>'
10761                 });
10762                 msgEl = bodyEl.dom.firstChild;
10763                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
10764                 textboxEl.enableDisplayMode();
10765                 textboxEl.addKeyListener([10,13], function(){
10766                     if(dlg.isVisible() && opt && opt.buttons){
10767                         if(opt.buttons.ok){
10768                             handleButton("ok");
10769                         }else if(opt.buttons.yes){
10770                             handleButton("yes");
10771                         }
10772                     }
10773                 });
10774                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
10775                 textareaEl.enableDisplayMode();
10776                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
10777                 progressEl.enableDisplayMode();
10778                 var pf = progressEl.dom.firstChild;
10779                 if (pf) {
10780                     pp = Roo.get(pf.firstChild);
10781                     pp.setHeight(pf.offsetHeight);
10782                 }
10783                 
10784             }
10785             return dlg;
10786         },
10787
10788         /**
10789          * Updates the message box body text
10790          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
10791          * the XHTML-compliant non-breaking space character '&amp;#160;')
10792          * @return {Roo.MessageBox} This message box
10793          */
10794         updateText : function(text){
10795             if(!dlg.isVisible() && !opt.width){
10796                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
10797             }
10798             msgEl.innerHTML = text || '&#160;';
10799       
10800             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
10801             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
10802             var w = Math.max(
10803                     Math.min(opt.width || cw , this.maxWidth), 
10804                     Math.max(opt.minWidth || this.minWidth, bwidth)
10805             );
10806             if(opt.prompt){
10807                 activeTextEl.setWidth(w);
10808             }
10809             if(dlg.isVisible()){
10810                 dlg.fixedcenter = false;
10811             }
10812             // to big, make it scroll. = But as usual stupid IE does not support
10813             // !important..
10814             
10815             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
10816                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
10817                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
10818             } else {
10819                 bodyEl.dom.style.height = '';
10820                 bodyEl.dom.style.overflowY = '';
10821             }
10822             if (cw > w) {
10823                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
10824             } else {
10825                 bodyEl.dom.style.overflowX = '';
10826             }
10827             
10828             dlg.setContentSize(w, bodyEl.getHeight());
10829             if(dlg.isVisible()){
10830                 dlg.fixedcenter = true;
10831             }
10832             return this;
10833         },
10834
10835         /**
10836          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
10837          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
10838          * @param {Number} value Any number between 0 and 1 (e.g., .5)
10839          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
10840          * @return {Roo.MessageBox} This message box
10841          */
10842         updateProgress : function(value, text){
10843             if(text){
10844                 this.updateText(text);
10845             }
10846             if (pp) { // weird bug on my firefox - for some reason this is not defined
10847                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
10848             }
10849             return this;
10850         },        
10851
10852         /**
10853          * Returns true if the message box is currently displayed
10854          * @return {Boolean} True if the message box is visible, else false
10855          */
10856         isVisible : function(){
10857             return dlg && dlg.isVisible();  
10858         },
10859
10860         /**
10861          * Hides the message box if it is displayed
10862          */
10863         hide : function(){
10864             if(this.isVisible()){
10865                 dlg.hide();
10866             }  
10867         },
10868
10869         /**
10870          * Displays a new message box, or reinitializes an existing message box, based on the config options
10871          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
10872          * The following config object properties are supported:
10873          * <pre>
10874 Property    Type             Description
10875 ----------  ---------------  ------------------------------------------------------------------------------------
10876 animEl            String/Element   An id or Element from which the message box should animate as it opens and
10877                                    closes (defaults to undefined)
10878 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
10879                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
10880 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
10881                                    progress and wait dialogs will ignore this property and always hide the
10882                                    close button as they can only be closed programmatically.
10883 cls               String           A custom CSS class to apply to the message box element
10884 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
10885                                    displayed (defaults to 75)
10886 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
10887                                    function will be btn (the name of the button that was clicked, if applicable,
10888                                    e.g. "ok"), and text (the value of the active text field, if applicable).
10889                                    Progress and wait dialogs will ignore this option since they do not respond to
10890                                    user actions and can only be closed programmatically, so any required function
10891                                    should be called by the same code after it closes the dialog.
10892 icon              String           A CSS class that provides a background image to be used as an icon for
10893                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
10894 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
10895 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
10896 modal             Boolean          False to allow user interaction with the page while the message box is
10897                                    displayed (defaults to true)
10898 msg               String           A string that will replace the existing message box body text (defaults
10899                                    to the XHTML-compliant non-breaking space character '&#160;')
10900 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
10901 progress          Boolean          True to display a progress bar (defaults to false)
10902 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
10903 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
10904 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
10905 title             String           The title text
10906 value             String           The string value to set into the active textbox element if displayed
10907 wait              Boolean          True to display a progress bar (defaults to false)
10908 width             Number           The width of the dialog in pixels
10909 </pre>
10910          *
10911          * Example usage:
10912          * <pre><code>
10913 Roo.Msg.show({
10914    title: 'Address',
10915    msg: 'Please enter your address:',
10916    width: 300,
10917    buttons: Roo.MessageBox.OKCANCEL,
10918    multiline: true,
10919    fn: saveAddress,
10920    animEl: 'addAddressBtn'
10921 });
10922 </code></pre>
10923          * @param {Object} config Configuration options
10924          * @return {Roo.MessageBox} This message box
10925          */
10926         show : function(options)
10927         {
10928             
10929             // this causes nightmares if you show one dialog after another
10930             // especially on callbacks..
10931              
10932             if(this.isVisible()){
10933                 
10934                 this.hide();
10935                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
10936                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
10937                 Roo.log("New Dialog Message:" +  options.msg )
10938                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
10939                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
10940                 
10941             }
10942             var d = this.getDialog();
10943             opt = options;
10944             d.setTitle(opt.title || "&#160;");
10945             d.close.setDisplayed(opt.closable !== false);
10946             activeTextEl = textboxEl;
10947             opt.prompt = opt.prompt || (opt.multiline ? true : false);
10948             if(opt.prompt){
10949                 if(opt.multiline){
10950                     textboxEl.hide();
10951                     textareaEl.show();
10952                     textareaEl.setHeight(typeof opt.multiline == "number" ?
10953                         opt.multiline : this.defaultTextHeight);
10954                     activeTextEl = textareaEl;
10955                 }else{
10956                     textboxEl.show();
10957                     textareaEl.hide();
10958                 }
10959             }else{
10960                 textboxEl.hide();
10961                 textareaEl.hide();
10962             }
10963             progressEl.setDisplayed(opt.progress === true);
10964             this.updateProgress(0);
10965             activeTextEl.dom.value = opt.value || "";
10966             if(opt.prompt){
10967                 dlg.setDefaultButton(activeTextEl);
10968             }else{
10969                 var bs = opt.buttons;
10970                 var db = null;
10971                 if(bs && bs.ok){
10972                     db = buttons["ok"];
10973                 }else if(bs && bs.yes){
10974                     db = buttons["yes"];
10975                 }
10976                 dlg.setDefaultButton(db);
10977             }
10978             bwidth = updateButtons(opt.buttons);
10979             this.updateText(opt.msg);
10980             if(opt.cls){
10981                 d.el.addClass(opt.cls);
10982             }
10983             d.proxyDrag = opt.proxyDrag === true;
10984             d.modal = opt.modal !== false;
10985             d.mask = opt.modal !== false ? mask : false;
10986             if(!d.isVisible()){
10987                 // force it to the end of the z-index stack so it gets a cursor in FF
10988                 document.body.appendChild(dlg.el.dom);
10989                 d.animateTarget = null;
10990                 d.show(options.animEl);
10991             }
10992             return this;
10993         },
10994
10995         /**
10996          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
10997          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
10998          * and closing the message box when the process is complete.
10999          * @param {String} title The title bar text
11000          * @param {String} msg The message box body text
11001          * @return {Roo.MessageBox} This message box
11002          */
11003         progress : function(title, msg){
11004             this.show({
11005                 title : title,
11006                 msg : msg,
11007                 buttons: false,
11008                 progress:true,
11009                 closable:false,
11010                 minWidth: this.minProgressWidth,
11011                 modal : true
11012             });
11013             return this;
11014         },
11015
11016         /**
11017          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
11018          * If a callback function is passed it will be called after the user clicks the button, and the
11019          * id of the button that was clicked will be passed as the only parameter to the callback
11020          * (could also be the top-right close button).
11021          * @param {String} title The title bar text
11022          * @param {String} msg The message box body text
11023          * @param {Function} fn (optional) The callback function invoked after the message box is closed
11024          * @param {Object} scope (optional) The scope of the callback function
11025          * @return {Roo.MessageBox} This message box
11026          */
11027         alert : function(title, msg, fn, scope){
11028             this.show({
11029                 title : title,
11030                 msg : msg,
11031                 buttons: this.OK,
11032                 fn: fn,
11033                 scope : scope,
11034                 modal : true
11035             });
11036             return this;
11037         },
11038
11039         /**
11040          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
11041          * interaction while waiting for a long-running process to complete that does not have defined intervals.
11042          * You are responsible for closing the message box when the process is complete.
11043          * @param {String} msg The message box body text
11044          * @param {String} title (optional) The title bar text
11045          * @return {Roo.MessageBox} This message box
11046          */
11047         wait : function(msg, title){
11048             this.show({
11049                 title : title,
11050                 msg : msg,
11051                 buttons: false,
11052                 closable:false,
11053                 progress:true,
11054                 modal:true,
11055                 width:300,
11056                 wait:true
11057             });
11058             waitTimer = Roo.TaskMgr.start({
11059                 run: function(i){
11060                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
11061                 },
11062                 interval: 1000
11063             });
11064             return this;
11065         },
11066
11067         /**
11068          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
11069          * If a callback function is passed it will be called after the user clicks either button, and the id of the
11070          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
11071          * @param {String} title The title bar text
11072          * @param {String} msg The message box body text
11073          * @param {Function} fn (optional) The callback function invoked after the message box is closed
11074          * @param {Object} scope (optional) The scope of the callback function
11075          * @return {Roo.MessageBox} This message box
11076          */
11077         confirm : function(title, msg, fn, scope){
11078             this.show({
11079                 title : title,
11080                 msg : msg,
11081                 buttons: this.YESNO,
11082                 fn: fn,
11083                 scope : scope,
11084                 modal : true
11085             });
11086             return this;
11087         },
11088
11089         /**
11090          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
11091          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
11092          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
11093          * (could also be the top-right close button) and the text that was entered will be passed as the two
11094          * parameters to the callback.
11095          * @param {String} title The title bar text
11096          * @param {String} msg The message box body text
11097          * @param {Function} fn (optional) The callback function invoked after the message box is closed
11098          * @param {Object} scope (optional) The scope of the callback function
11099          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
11100          * property, or the height in pixels to create the textbox (defaults to false / single-line)
11101          * @return {Roo.MessageBox} This message box
11102          */
11103         prompt : function(title, msg, fn, scope, multiline){
11104             this.show({
11105                 title : title,
11106                 msg : msg,
11107                 buttons: this.OKCANCEL,
11108                 fn: fn,
11109                 minWidth:250,
11110                 scope : scope,
11111                 prompt:true,
11112                 multiline: multiline,
11113                 modal : true
11114             });
11115             return this;
11116         },
11117
11118         /**
11119          * Button config that displays a single OK button
11120          * @type Object
11121          */
11122         OK : {ok:true},
11123         /**
11124          * Button config that displays Yes and No buttons
11125          * @type Object
11126          */
11127         YESNO : {yes:true, no:true},
11128         /**
11129          * Button config that displays OK and Cancel buttons
11130          * @type Object
11131          */
11132         OKCANCEL : {ok:true, cancel:true},
11133         /**
11134          * Button config that displays Yes, No and Cancel buttons
11135          * @type Object
11136          */
11137         YESNOCANCEL : {yes:true, no:true, cancel:true},
11138
11139         /**
11140          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
11141          * @type Number
11142          */
11143         defaultTextHeight : 75,
11144         /**
11145          * The maximum width in pixels of the message box (defaults to 600)
11146          * @type Number
11147          */
11148         maxWidth : 600,
11149         /**
11150          * The minimum width in pixels of the message box (defaults to 100)
11151          * @type Number
11152          */
11153         minWidth : 100,
11154         /**
11155          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
11156          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
11157          * @type Number
11158          */
11159         minProgressWidth : 250,
11160         /**
11161          * An object containing the default button text strings that can be overriden for localized language support.
11162          * Supported properties are: ok, cancel, yes and no.
11163          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
11164          * @type Object
11165          */
11166         buttonText : {
11167             ok : "OK",
11168             cancel : "Cancel",
11169             yes : "Yes",
11170             no : "No"
11171         }
11172     };
11173 }();
11174
11175 /**
11176  * Shorthand for {@link Roo.MessageBox}
11177  */
11178 Roo.Msg = Roo.MessageBox;/*
11179  * Based on:
11180  * Ext JS Library 1.1.1
11181  * Copyright(c) 2006-2007, Ext JS, LLC.
11182  *
11183  * Originally Released Under LGPL - original licence link has changed is not relivant.
11184  *
11185  * Fork - LGPL
11186  * <script type="text/javascript">
11187  */
11188 /**
11189  * @class Roo.QuickTips
11190  * Provides attractive and customizable tooltips for any element.
11191  * @singleton
11192  */
11193 Roo.QuickTips = function(){
11194     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
11195     var ce, bd, xy, dd;
11196     var visible = false, disabled = true, inited = false;
11197     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
11198     
11199     var onOver = function(e){
11200         if(disabled){
11201             return;
11202         }
11203         var t = e.getTarget();
11204         if(!t || t.nodeType !== 1 || t == document || t == document.body){
11205             return;
11206         }
11207         if(ce && t == ce.el){
11208             clearTimeout(hideProc);
11209             return;
11210         }
11211         if(t && tagEls[t.id]){
11212             tagEls[t.id].el = t;
11213             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
11214             return;
11215         }
11216         var ttp, et = Roo.fly(t);
11217         var ns = cfg.namespace;
11218         if(tm.interceptTitles && t.title){
11219             ttp = t.title;
11220             t.qtip = ttp;
11221             t.removeAttribute("title");
11222             e.preventDefault();
11223         }else{
11224             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
11225         }
11226         if(ttp){
11227             showProc = show.defer(tm.showDelay, tm, [{
11228                 el: t, 
11229                 text: ttp.replace(/\\n/g,'<br/>'),
11230                 width: et.getAttributeNS(ns, cfg.width),
11231                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
11232                 title: et.getAttributeNS(ns, cfg.title),
11233                     cls: et.getAttributeNS(ns, cfg.cls)
11234             }]);
11235         }
11236     };
11237     
11238     var onOut = function(e){
11239         clearTimeout(showProc);
11240         var t = e.getTarget();
11241         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
11242             hideProc = setTimeout(hide, tm.hideDelay);
11243         }
11244     };
11245     
11246     var onMove = function(e){
11247         if(disabled){
11248             return;
11249         }
11250         xy = e.getXY();
11251         xy[1] += 18;
11252         if(tm.trackMouse && ce){
11253             el.setXY(xy);
11254         }
11255     };
11256     
11257     var onDown = function(e){
11258         clearTimeout(showProc);
11259         clearTimeout(hideProc);
11260         if(!e.within(el)){
11261             if(tm.hideOnClick){
11262                 hide();
11263                 tm.disable();
11264                 tm.enable.defer(100, tm);
11265             }
11266         }
11267     };
11268     
11269     var getPad = function(){
11270         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
11271     };
11272
11273     var show = function(o){
11274         if(disabled){
11275             return;
11276         }
11277         clearTimeout(dismissProc);
11278         ce = o;
11279         if(removeCls){ // in case manually hidden
11280             el.removeClass(removeCls);
11281             removeCls = null;
11282         }
11283         if(ce.cls){
11284             el.addClass(ce.cls);
11285             removeCls = ce.cls;
11286         }
11287         if(ce.title){
11288             tipTitle.update(ce.title);
11289             tipTitle.show();
11290         }else{
11291             tipTitle.update('');
11292             tipTitle.hide();
11293         }
11294         el.dom.style.width  = tm.maxWidth+'px';
11295         //tipBody.dom.style.width = '';
11296         tipBodyText.update(o.text);
11297         var p = getPad(), w = ce.width;
11298         if(!w){
11299             var td = tipBodyText.dom;
11300             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
11301             if(aw > tm.maxWidth){
11302                 w = tm.maxWidth;
11303             }else if(aw < tm.minWidth){
11304                 w = tm.minWidth;
11305             }else{
11306                 w = aw;
11307             }
11308         }
11309         //tipBody.setWidth(w);
11310         el.setWidth(parseInt(w, 10) + p);
11311         if(ce.autoHide === false){
11312             close.setDisplayed(true);
11313             if(dd){
11314                 dd.unlock();
11315             }
11316         }else{
11317             close.setDisplayed(false);
11318             if(dd){
11319                 dd.lock();
11320             }
11321         }
11322         if(xy){
11323             el.avoidY = xy[1]-18;
11324             el.setXY(xy);
11325         }
11326         if(tm.animate){
11327             el.setOpacity(.1);
11328             el.setStyle("visibility", "visible");
11329             el.fadeIn({callback: afterShow});
11330         }else{
11331             afterShow();
11332         }
11333     };
11334     
11335     var afterShow = function(){
11336         if(ce){
11337             el.show();
11338             esc.enable();
11339             if(tm.autoDismiss && ce.autoHide !== false){
11340                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
11341             }
11342         }
11343     };
11344     
11345     var hide = function(noanim){
11346         clearTimeout(dismissProc);
11347         clearTimeout(hideProc);
11348         ce = null;
11349         if(el.isVisible()){
11350             esc.disable();
11351             if(noanim !== true && tm.animate){
11352                 el.fadeOut({callback: afterHide});
11353             }else{
11354                 afterHide();
11355             } 
11356         }
11357     };
11358     
11359     var afterHide = function(){
11360         el.hide();
11361         if(removeCls){
11362             el.removeClass(removeCls);
11363             removeCls = null;
11364         }
11365     };
11366     
11367     return {
11368         /**
11369         * @cfg {Number} minWidth
11370         * The minimum width of the quick tip (defaults to 40)
11371         */
11372        minWidth : 40,
11373         /**
11374         * @cfg {Number} maxWidth
11375         * The maximum width of the quick tip (defaults to 300)
11376         */
11377        maxWidth : 300,
11378         /**
11379         * @cfg {Boolean} interceptTitles
11380         * True to automatically use the element's DOM title value if available (defaults to false)
11381         */
11382        interceptTitles : false,
11383         /**
11384         * @cfg {Boolean} trackMouse
11385         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
11386         */
11387        trackMouse : false,
11388         /**
11389         * @cfg {Boolean} hideOnClick
11390         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
11391         */
11392        hideOnClick : true,
11393         /**
11394         * @cfg {Number} showDelay
11395         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
11396         */
11397        showDelay : 500,
11398         /**
11399         * @cfg {Number} hideDelay
11400         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
11401         */
11402        hideDelay : 200,
11403         /**
11404         * @cfg {Boolean} autoHide
11405         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
11406         * Used in conjunction with hideDelay.
11407         */
11408        autoHide : true,
11409         /**
11410         * @cfg {Boolean}
11411         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
11412         * (defaults to true).  Used in conjunction with autoDismissDelay.
11413         */
11414        autoDismiss : true,
11415         /**
11416         * @cfg {Number}
11417         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
11418         */
11419        autoDismissDelay : 5000,
11420        /**
11421         * @cfg {Boolean} animate
11422         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
11423         */
11424        animate : false,
11425
11426        /**
11427         * @cfg {String} title
11428         * Title text to display (defaults to '').  This can be any valid HTML markup.
11429         */
11430         title: '',
11431        /**
11432         * @cfg {String} text
11433         * Body text to display (defaults to '').  This can be any valid HTML markup.
11434         */
11435         text : '',
11436        /**
11437         * @cfg {String} cls
11438         * A CSS class to apply to the base quick tip element (defaults to '').
11439         */
11440         cls : '',
11441        /**
11442         * @cfg {Number} width
11443         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
11444         * minWidth or maxWidth.
11445         */
11446         width : null,
11447
11448     /**
11449      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
11450      * or display QuickTips in a page.
11451      */
11452        init : function(){
11453           tm = Roo.QuickTips;
11454           cfg = tm.tagConfig;
11455           if(!inited){
11456               if(!Roo.isReady){ // allow calling of init() before onReady
11457                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
11458                   return;
11459               }
11460               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
11461               el.fxDefaults = {stopFx: true};
11462               // maximum custom styling
11463               //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>');
11464               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>');              
11465               tipTitle = el.child('h3');
11466               tipTitle.enableDisplayMode("block");
11467               tipBody = el.child('div.x-tip-bd');
11468               tipBodyText = el.child('div.x-tip-bd-inner');
11469               //bdLeft = el.child('div.x-tip-bd-left');
11470               //bdRight = el.child('div.x-tip-bd-right');
11471               close = el.child('div.x-tip-close');
11472               close.enableDisplayMode("block");
11473               close.on("click", hide);
11474               var d = Roo.get(document);
11475               d.on("mousedown", onDown);
11476               d.on("mouseover", onOver);
11477               d.on("mouseout", onOut);
11478               d.on("mousemove", onMove);
11479               esc = d.addKeyListener(27, hide);
11480               esc.disable();
11481               if(Roo.dd.DD){
11482                   dd = el.initDD("default", null, {
11483                       onDrag : function(){
11484                           el.sync();  
11485                       }
11486                   });
11487                   dd.setHandleElId(tipTitle.id);
11488                   dd.lock();
11489               }
11490               inited = true;
11491           }
11492           this.enable(); 
11493        },
11494
11495     /**
11496      * Configures a new quick tip instance and assigns it to a target element.  The following config options
11497      * are supported:
11498      * <pre>
11499 Property    Type                   Description
11500 ----------  ---------------------  ------------------------------------------------------------------------
11501 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
11502      * </ul>
11503      * @param {Object} config The config object
11504      */
11505        register : function(config){
11506            var cs = config instanceof Array ? config : arguments;
11507            for(var i = 0, len = cs.length; i < len; i++) {
11508                var c = cs[i];
11509                var target = c.target;
11510                if(target){
11511                    if(target instanceof Array){
11512                        for(var j = 0, jlen = target.length; j < jlen; j++){
11513                            tagEls[target[j]] = c;
11514                        }
11515                    }else{
11516                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
11517                    }
11518                }
11519            }
11520        },
11521
11522     /**
11523      * Removes this quick tip from its element and destroys it.
11524      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
11525      */
11526        unregister : function(el){
11527            delete tagEls[Roo.id(el)];
11528        },
11529
11530     /**
11531      * Enable this quick tip.
11532      */
11533        enable : function(){
11534            if(inited && disabled){
11535                locks.pop();
11536                if(locks.length < 1){
11537                    disabled = false;
11538                }
11539            }
11540        },
11541
11542     /**
11543      * Disable this quick tip.
11544      */
11545        disable : function(){
11546           disabled = true;
11547           clearTimeout(showProc);
11548           clearTimeout(hideProc);
11549           clearTimeout(dismissProc);
11550           if(ce){
11551               hide(true);
11552           }
11553           locks.push(1);
11554        },
11555
11556     /**
11557      * Returns true if the quick tip is enabled, else false.
11558      */
11559        isEnabled : function(){
11560             return !disabled;
11561        },
11562
11563         // private
11564        tagConfig : {
11565            namespace : "roo", // was ext?? this may break..
11566            alt_namespace : "ext",
11567            attribute : "qtip",
11568            width : "width",
11569            target : "target",
11570            title : "qtitle",
11571            hide : "hide",
11572            cls : "qclass"
11573        }
11574    };
11575 }();
11576
11577 // backwards compat
11578 Roo.QuickTips.tips = Roo.QuickTips.register;/*
11579  * Based on:
11580  * Ext JS Library 1.1.1
11581  * Copyright(c) 2006-2007, Ext JS, LLC.
11582  *
11583  * Originally Released Under LGPL - original licence link has changed is not relivant.
11584  *
11585  * Fork - LGPL
11586  * <script type="text/javascript">
11587  */
11588  
11589
11590 /**
11591  * @class Roo.tree.TreePanel
11592  * @extends Roo.data.Tree
11593
11594  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
11595  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
11596  * @cfg {Boolean} enableDD true to enable drag and drop
11597  * @cfg {Boolean} enableDrag true to enable just drag
11598  * @cfg {Boolean} enableDrop true to enable just drop
11599  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
11600  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
11601  * @cfg {String} ddGroup The DD group this TreePanel belongs to
11602  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
11603  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
11604  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
11605  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
11606  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
11607  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
11608  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
11609  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
11610  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
11611  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
11612  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
11613  * @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>
11614  * @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>
11615  * 
11616  * @constructor
11617  * @param {String/HTMLElement/Element} el The container element
11618  * @param {Object} config
11619  */
11620 Roo.tree.TreePanel = function(el, config){
11621     var root = false;
11622     var loader = false;
11623     if (config.root) {
11624         root = config.root;
11625         delete config.root;
11626     }
11627     if (config.loader) {
11628         loader = config.loader;
11629         delete config.loader;
11630     }
11631     
11632     Roo.apply(this, config);
11633     Roo.tree.TreePanel.superclass.constructor.call(this);
11634     this.el = Roo.get(el);
11635     this.el.addClass('x-tree');
11636     //console.log(root);
11637     if (root) {
11638         this.setRootNode( Roo.factory(root, Roo.tree));
11639     }
11640     if (loader) {
11641         this.loader = Roo.factory(loader, Roo.tree);
11642     }
11643    /**
11644     * Read-only. The id of the container element becomes this TreePanel's id.
11645     */
11646     this.id = this.el.id;
11647     this.addEvents({
11648         /**
11649         * @event beforeload
11650         * Fires before a node is loaded, return false to cancel
11651         * @param {Node} node The node being loaded
11652         */
11653         "beforeload" : true,
11654         /**
11655         * @event load
11656         * Fires when a node is loaded
11657         * @param {Node} node The node that was loaded
11658         */
11659         "load" : true,
11660         /**
11661         * @event textchange
11662         * Fires when the text for a node is changed
11663         * @param {Node} node The node
11664         * @param {String} text The new text
11665         * @param {String} oldText The old text
11666         */
11667         "textchange" : true,
11668         /**
11669         * @event beforeexpand
11670         * Fires before a node is expanded, return false to cancel.
11671         * @param {Node} node The node
11672         * @param {Boolean} deep
11673         * @param {Boolean} anim
11674         */
11675         "beforeexpand" : true,
11676         /**
11677         * @event beforecollapse
11678         * Fires before a node is collapsed, return false to cancel.
11679         * @param {Node} node The node
11680         * @param {Boolean} deep
11681         * @param {Boolean} anim
11682         */
11683         "beforecollapse" : true,
11684         /**
11685         * @event expand
11686         * Fires when a node is expanded
11687         * @param {Node} node The node
11688         */
11689         "expand" : true,
11690         /**
11691         * @event disabledchange
11692         * Fires when the disabled status of a node changes
11693         * @param {Node} node The node
11694         * @param {Boolean} disabled
11695         */
11696         "disabledchange" : true,
11697         /**
11698         * @event collapse
11699         * Fires when a node is collapsed
11700         * @param {Node} node The node
11701         */
11702         "collapse" : true,
11703         /**
11704         * @event beforeclick
11705         * Fires before click processing on a node. Return false to cancel the default action.
11706         * @param {Node} node The node
11707         * @param {Roo.EventObject} e The event object
11708         */
11709         "beforeclick":true,
11710         /**
11711         * @event checkchange
11712         * Fires when a node with a checkbox's checked property changes
11713         * @param {Node} this This node
11714         * @param {Boolean} checked
11715         */
11716         "checkchange":true,
11717         /**
11718         * @event click
11719         * Fires when a node is clicked
11720         * @param {Node} node The node
11721         * @param {Roo.EventObject} e The event object
11722         */
11723         "click":true,
11724         /**
11725         * @event dblclick
11726         * Fires when a node is double clicked
11727         * @param {Node} node The node
11728         * @param {Roo.EventObject} e The event object
11729         */
11730         "dblclick":true,
11731         /**
11732         * @event contextmenu
11733         * Fires when a node is right clicked
11734         * @param {Node} node The node
11735         * @param {Roo.EventObject} e The event object
11736         */
11737         "contextmenu":true,
11738         /**
11739         * @event beforechildrenrendered
11740         * Fires right before the child nodes for a node are rendered
11741         * @param {Node} node The node
11742         */
11743         "beforechildrenrendered":true,
11744         /**
11745         * @event startdrag
11746         * Fires when a node starts being dragged
11747         * @param {Roo.tree.TreePanel} this
11748         * @param {Roo.tree.TreeNode} node
11749         * @param {event} e The raw browser event
11750         */ 
11751        "startdrag" : true,
11752        /**
11753         * @event enddrag
11754         * Fires when a drag operation is complete
11755         * @param {Roo.tree.TreePanel} this
11756         * @param {Roo.tree.TreeNode} node
11757         * @param {event} e The raw browser event
11758         */
11759        "enddrag" : true,
11760        /**
11761         * @event dragdrop
11762         * Fires when a dragged node is dropped on a valid DD target
11763         * @param {Roo.tree.TreePanel} this
11764         * @param {Roo.tree.TreeNode} node
11765         * @param {DD} dd The dd it was dropped on
11766         * @param {event} e The raw browser event
11767         */
11768        "dragdrop" : true,
11769        /**
11770         * @event beforenodedrop
11771         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
11772         * passed to handlers has the following properties:<br />
11773         * <ul style="padding:5px;padding-left:16px;">
11774         * <li>tree - The TreePanel</li>
11775         * <li>target - The node being targeted for the drop</li>
11776         * <li>data - The drag data from the drag source</li>
11777         * <li>point - The point of the drop - append, above or below</li>
11778         * <li>source - The drag source</li>
11779         * <li>rawEvent - Raw mouse event</li>
11780         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
11781         * to be inserted by setting them on this object.</li>
11782         * <li>cancel - Set this to true to cancel the drop.</li>
11783         * </ul>
11784         * @param {Object} dropEvent
11785         */
11786        "beforenodedrop" : true,
11787        /**
11788         * @event nodedrop
11789         * Fires after a DD object is dropped on a node in this tree. The dropEvent
11790         * passed to handlers has the following properties:<br />
11791         * <ul style="padding:5px;padding-left:16px;">
11792         * <li>tree - The TreePanel</li>
11793         * <li>target - The node being targeted for the drop</li>
11794         * <li>data - The drag data from the drag source</li>
11795         * <li>point - The point of the drop - append, above or below</li>
11796         * <li>source - The drag source</li>
11797         * <li>rawEvent - Raw mouse event</li>
11798         * <li>dropNode - Dropped node(s).</li>
11799         * </ul>
11800         * @param {Object} dropEvent
11801         */
11802        "nodedrop" : true,
11803         /**
11804         * @event nodedragover
11805         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
11806         * passed to handlers has the following properties:<br />
11807         * <ul style="padding:5px;padding-left:16px;">
11808         * <li>tree - The TreePanel</li>
11809         * <li>target - The node being targeted for the drop</li>
11810         * <li>data - The drag data from the drag source</li>
11811         * <li>point - The point of the drop - append, above or below</li>
11812         * <li>source - The drag source</li>
11813         * <li>rawEvent - Raw mouse event</li>
11814         * <li>dropNode - Drop node(s) provided by the source.</li>
11815         * <li>cancel - Set this to true to signal drop not allowed.</li>
11816         * </ul>
11817         * @param {Object} dragOverEvent
11818         */
11819        "nodedragover" : true
11820         
11821     });
11822     if(this.singleExpand){
11823        this.on("beforeexpand", this.restrictExpand, this);
11824     }
11825     if (this.editor) {
11826         this.editor.tree = this;
11827         this.editor = Roo.factory(this.editor, Roo.tree);
11828     }
11829     
11830     if (this.selModel) {
11831         this.selModel = Roo.factory(this.selModel, Roo.tree);
11832     }
11833    
11834 };
11835 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
11836     rootVisible : true,
11837     animate: Roo.enableFx,
11838     lines : true,
11839     enableDD : false,
11840     hlDrop : Roo.enableFx,
11841   
11842     renderer: false,
11843     
11844     rendererTip: false,
11845     // private
11846     restrictExpand : function(node){
11847         var p = node.parentNode;
11848         if(p){
11849             if(p.expandedChild && p.expandedChild.parentNode == p){
11850                 p.expandedChild.collapse();
11851             }
11852             p.expandedChild = node;
11853         }
11854     },
11855
11856     // private override
11857     setRootNode : function(node){
11858         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
11859         if(!this.rootVisible){
11860             node.ui = new Roo.tree.RootTreeNodeUI(node);
11861         }
11862         return node;
11863     },
11864
11865     /**
11866      * Returns the container element for this TreePanel
11867      */
11868     getEl : function(){
11869         return this.el;
11870     },
11871
11872     /**
11873      * Returns the default TreeLoader for this TreePanel
11874      */
11875     getLoader : function(){
11876         return this.loader;
11877     },
11878
11879     /**
11880      * Expand all nodes
11881      */
11882     expandAll : function(){
11883         this.root.expand(true);
11884     },
11885
11886     /**
11887      * Collapse all nodes
11888      */
11889     collapseAll : function(){
11890         this.root.collapse(true);
11891     },
11892
11893     /**
11894      * Returns the selection model used by this TreePanel
11895      */
11896     getSelectionModel : function(){
11897         if(!this.selModel){
11898             this.selModel = new Roo.tree.DefaultSelectionModel();
11899         }
11900         return this.selModel;
11901     },
11902
11903     /**
11904      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
11905      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
11906      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
11907      * @return {Array}
11908      */
11909     getChecked : function(a, startNode){
11910         startNode = startNode || this.root;
11911         var r = [];
11912         var f = function(){
11913             if(this.attributes.checked){
11914                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
11915             }
11916         }
11917         startNode.cascade(f);
11918         return r;
11919     },
11920
11921     /**
11922      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11923      * @param {String} path
11924      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11925      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
11926      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
11927      */
11928     expandPath : function(path, attr, callback){
11929         attr = attr || "id";
11930         var keys = path.split(this.pathSeparator);
11931         var curNode = this.root;
11932         if(curNode.attributes[attr] != keys[1]){ // invalid root
11933             if(callback){
11934                 callback(false, null);
11935             }
11936             return;
11937         }
11938         var index = 1;
11939         var f = function(){
11940             if(++index == keys.length){
11941                 if(callback){
11942                     callback(true, curNode);
11943                 }
11944                 return;
11945             }
11946             var c = curNode.findChild(attr, keys[index]);
11947             if(!c){
11948                 if(callback){
11949                     callback(false, curNode);
11950                 }
11951                 return;
11952             }
11953             curNode = c;
11954             c.expand(false, false, f);
11955         };
11956         curNode.expand(false, false, f);
11957     },
11958
11959     /**
11960      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11961      * @param {String} path
11962      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11963      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
11964      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
11965      */
11966     selectPath : function(path, attr, callback){
11967         attr = attr || "id";
11968         var keys = path.split(this.pathSeparator);
11969         var v = keys.pop();
11970         if(keys.length > 0){
11971             var f = function(success, node){
11972                 if(success && node){
11973                     var n = node.findChild(attr, v);
11974                     if(n){
11975                         n.select();
11976                         if(callback){
11977                             callback(true, n);
11978                         }
11979                     }else if(callback){
11980                         callback(false, n);
11981                     }
11982                 }else{
11983                     if(callback){
11984                         callback(false, n);
11985                     }
11986                 }
11987             };
11988             this.expandPath(keys.join(this.pathSeparator), attr, f);
11989         }else{
11990             this.root.select();
11991             if(callback){
11992                 callback(true, this.root);
11993             }
11994         }
11995     },
11996
11997     getTreeEl : function(){
11998         return this.el;
11999     },
12000
12001     /**
12002      * Trigger rendering of this TreePanel
12003      */
12004     render : function(){
12005         if (this.innerCt) {
12006             return this; // stop it rendering more than once!!
12007         }
12008         
12009         this.innerCt = this.el.createChild({tag:"ul",
12010                cls:"x-tree-root-ct " +
12011                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
12012
12013         if(this.containerScroll){
12014             Roo.dd.ScrollManager.register(this.el);
12015         }
12016         if((this.enableDD || this.enableDrop) && !this.dropZone){
12017            /**
12018             * The dropZone used by this tree if drop is enabled
12019             * @type Roo.tree.TreeDropZone
12020             */
12021              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
12022                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
12023            });
12024         }
12025         if((this.enableDD || this.enableDrag) && !this.dragZone){
12026            /**
12027             * The dragZone used by this tree if drag is enabled
12028             * @type Roo.tree.TreeDragZone
12029             */
12030             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
12031                ddGroup: this.ddGroup || "TreeDD",
12032                scroll: this.ddScroll
12033            });
12034         }
12035         this.getSelectionModel().init(this);
12036         if (!this.root) {
12037             Roo.log("ROOT not set in tree");
12038             return this;
12039         }
12040         this.root.render();
12041         if(!this.rootVisible){
12042             this.root.renderChildren();
12043         }
12044         return this;
12045     }
12046 });/*
12047  * Based on:
12048  * Ext JS Library 1.1.1
12049  * Copyright(c) 2006-2007, Ext JS, LLC.
12050  *
12051  * Originally Released Under LGPL - original licence link has changed is not relivant.
12052  *
12053  * Fork - LGPL
12054  * <script type="text/javascript">
12055  */
12056  
12057
12058 /**
12059  * @class Roo.tree.DefaultSelectionModel
12060  * @extends Roo.util.Observable
12061  * The default single selection for a TreePanel.
12062  * @param {Object} cfg Configuration
12063  */
12064 Roo.tree.DefaultSelectionModel = function(cfg){
12065    this.selNode = null;
12066    
12067    
12068    
12069    this.addEvents({
12070        /**
12071         * @event selectionchange
12072         * Fires when the selected node changes
12073         * @param {DefaultSelectionModel} this
12074         * @param {TreeNode} node the new selection
12075         */
12076        "selectionchange" : true,
12077
12078        /**
12079         * @event beforeselect
12080         * Fires before the selected node changes, return false to cancel the change
12081         * @param {DefaultSelectionModel} this
12082         * @param {TreeNode} node the new selection
12083         * @param {TreeNode} node the old selection
12084         */
12085        "beforeselect" : true
12086    });
12087    
12088     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
12089 };
12090
12091 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
12092     init : function(tree){
12093         this.tree = tree;
12094         tree.getTreeEl().on("keydown", this.onKeyDown, this);
12095         tree.on("click", this.onNodeClick, this);
12096     },
12097     
12098     onNodeClick : function(node, e){
12099         if (e.ctrlKey && this.selNode == node)  {
12100             this.unselect(node);
12101             return;
12102         }
12103         this.select(node);
12104     },
12105     
12106     /**
12107      * Select a node.
12108      * @param {TreeNode} node The node to select
12109      * @return {TreeNode} The selected node
12110      */
12111     select : function(node){
12112         var last = this.selNode;
12113         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
12114             if(last){
12115                 last.ui.onSelectedChange(false);
12116             }
12117             this.selNode = node;
12118             node.ui.onSelectedChange(true);
12119             this.fireEvent("selectionchange", this, node, last);
12120         }
12121         return node;
12122     },
12123     
12124     /**
12125      * Deselect a node.
12126      * @param {TreeNode} node The node to unselect
12127      */
12128     unselect : function(node){
12129         if(this.selNode == node){
12130             this.clearSelections();
12131         }    
12132     },
12133     
12134     /**
12135      * Clear all selections
12136      */
12137     clearSelections : function(){
12138         var n = this.selNode;
12139         if(n){
12140             n.ui.onSelectedChange(false);
12141             this.selNode = null;
12142             this.fireEvent("selectionchange", this, null);
12143         }
12144         return n;
12145     },
12146     
12147     /**
12148      * Get the selected node
12149      * @return {TreeNode} The selected node
12150      */
12151     getSelectedNode : function(){
12152         return this.selNode;    
12153     },
12154     
12155     /**
12156      * Returns true if the node is selected
12157      * @param {TreeNode} node The node to check
12158      * @return {Boolean}
12159      */
12160     isSelected : function(node){
12161         return this.selNode == node;  
12162     },
12163
12164     /**
12165      * Selects the node above the selected node in the tree, intelligently walking the nodes
12166      * @return TreeNode The new selection
12167      */
12168     selectPrevious : function(){
12169         var s = this.selNode || this.lastSelNode;
12170         if(!s){
12171             return null;
12172         }
12173         var ps = s.previousSibling;
12174         if(ps){
12175             if(!ps.isExpanded() || ps.childNodes.length < 1){
12176                 return this.select(ps);
12177             } else{
12178                 var lc = ps.lastChild;
12179                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
12180                     lc = lc.lastChild;
12181                 }
12182                 return this.select(lc);
12183             }
12184         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
12185             return this.select(s.parentNode);
12186         }
12187         return null;
12188     },
12189
12190     /**
12191      * Selects the node above the selected node in the tree, intelligently walking the nodes
12192      * @return TreeNode The new selection
12193      */
12194     selectNext : function(){
12195         var s = this.selNode || this.lastSelNode;
12196         if(!s){
12197             return null;
12198         }
12199         if(s.firstChild && s.isExpanded()){
12200              return this.select(s.firstChild);
12201          }else if(s.nextSibling){
12202              return this.select(s.nextSibling);
12203          }else if(s.parentNode){
12204             var newS = null;
12205             s.parentNode.bubble(function(){
12206                 if(this.nextSibling){
12207                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
12208                     return false;
12209                 }
12210             });
12211             return newS;
12212          }
12213         return null;
12214     },
12215
12216     onKeyDown : function(e){
12217         var s = this.selNode || this.lastSelNode;
12218         // undesirable, but required
12219         var sm = this;
12220         if(!s){
12221             return;
12222         }
12223         var k = e.getKey();
12224         switch(k){
12225              case e.DOWN:
12226                  e.stopEvent();
12227                  this.selectNext();
12228              break;
12229              case e.UP:
12230                  e.stopEvent();
12231                  this.selectPrevious();
12232              break;
12233              case e.RIGHT:
12234                  e.preventDefault();
12235                  if(s.hasChildNodes()){
12236                      if(!s.isExpanded()){
12237                          s.expand();
12238                      }else if(s.firstChild){
12239                          this.select(s.firstChild, e);
12240                      }
12241                  }
12242              break;
12243              case e.LEFT:
12244                  e.preventDefault();
12245                  if(s.hasChildNodes() && s.isExpanded()){
12246                      s.collapse();
12247                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
12248                      this.select(s.parentNode, e);
12249                  }
12250              break;
12251         };
12252     }
12253 });
12254
12255 /**
12256  * @class Roo.tree.MultiSelectionModel
12257  * @extends Roo.util.Observable
12258  * Multi selection for a TreePanel.
12259  * @param {Object} cfg Configuration
12260  */
12261 Roo.tree.MultiSelectionModel = function(){
12262    this.selNodes = [];
12263    this.selMap = {};
12264    this.addEvents({
12265        /**
12266         * @event selectionchange
12267         * Fires when the selected nodes change
12268         * @param {MultiSelectionModel} this
12269         * @param {Array} nodes Array of the selected nodes
12270         */
12271        "selectionchange" : true
12272    });
12273    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
12274    
12275 };
12276
12277 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
12278     init : function(tree){
12279         this.tree = tree;
12280         tree.getTreeEl().on("keydown", this.onKeyDown, this);
12281         tree.on("click", this.onNodeClick, this);
12282     },
12283     
12284     onNodeClick : function(node, e){
12285         this.select(node, e, e.ctrlKey);
12286     },
12287     
12288     /**
12289      * Select a node.
12290      * @param {TreeNode} node The node to select
12291      * @param {EventObject} e (optional) An event associated with the selection
12292      * @param {Boolean} keepExisting True to retain existing selections
12293      * @return {TreeNode} The selected node
12294      */
12295     select : function(node, e, keepExisting){
12296         if(keepExisting !== true){
12297             this.clearSelections(true);
12298         }
12299         if(this.isSelected(node)){
12300             this.lastSelNode = node;
12301             return node;
12302         }
12303         this.selNodes.push(node);
12304         this.selMap[node.id] = node;
12305         this.lastSelNode = node;
12306         node.ui.onSelectedChange(true);
12307         this.fireEvent("selectionchange", this, this.selNodes);
12308         return node;
12309     },
12310     
12311     /**
12312      * Deselect a node.
12313      * @param {TreeNode} node The node to unselect
12314      */
12315     unselect : function(node){
12316         if(this.selMap[node.id]){
12317             node.ui.onSelectedChange(false);
12318             var sn = this.selNodes;
12319             var index = -1;
12320             if(sn.indexOf){
12321                 index = sn.indexOf(node);
12322             }else{
12323                 for(var i = 0, len = sn.length; i < len; i++){
12324                     if(sn[i] == node){
12325                         index = i;
12326                         break;
12327                     }
12328                 }
12329             }
12330             if(index != -1){
12331                 this.selNodes.splice(index, 1);
12332             }
12333             delete this.selMap[node.id];
12334             this.fireEvent("selectionchange", this, this.selNodes);
12335         }
12336     },
12337     
12338     /**
12339      * Clear all selections
12340      */
12341     clearSelections : function(suppressEvent){
12342         var sn = this.selNodes;
12343         if(sn.length > 0){
12344             for(var i = 0, len = sn.length; i < len; i++){
12345                 sn[i].ui.onSelectedChange(false);
12346             }
12347             this.selNodes = [];
12348             this.selMap = {};
12349             if(suppressEvent !== true){
12350                 this.fireEvent("selectionchange", this, this.selNodes);
12351             }
12352         }
12353     },
12354     
12355     /**
12356      * Returns true if the node is selected
12357      * @param {TreeNode} node The node to check
12358      * @return {Boolean}
12359      */
12360     isSelected : function(node){
12361         return this.selMap[node.id] ? true : false;  
12362     },
12363     
12364     /**
12365      * Returns an array of the selected nodes
12366      * @return {Array}
12367      */
12368     getSelectedNodes : function(){
12369         return this.selNodes;    
12370     },
12371
12372     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
12373
12374     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
12375
12376     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
12377 });/*
12378  * Based on:
12379  * Ext JS Library 1.1.1
12380  * Copyright(c) 2006-2007, Ext JS, LLC.
12381  *
12382  * Originally Released Under LGPL - original licence link has changed is not relivant.
12383  *
12384  * Fork - LGPL
12385  * <script type="text/javascript">
12386  */
12387  
12388 /**
12389  * @class Roo.tree.TreeNode
12390  * @extends Roo.data.Node
12391  * @cfg {String} text The text for this node
12392  * @cfg {Boolean} expanded true to start the node expanded
12393  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
12394  * @cfg {Boolean} allowDrop false if this node cannot be drop on
12395  * @cfg {Boolean} disabled true to start the node disabled
12396  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
12397  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
12398  * @cfg {String} cls A css class to be added to the node
12399  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
12400  * @cfg {String} href URL of the link used for the node (defaults to #)
12401  * @cfg {String} hrefTarget target frame for the link
12402  * @cfg {String} qtip An Ext QuickTip for the node
12403  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
12404  * @cfg {Boolean} singleClickExpand True for single click expand on this node
12405  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
12406  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
12407  * (defaults to undefined with no checkbox rendered)
12408  * @constructor
12409  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
12410  */
12411 Roo.tree.TreeNode = function(attributes){
12412     attributes = attributes || {};
12413     if(typeof attributes == "string"){
12414         attributes = {text: attributes};
12415     }
12416     this.childrenRendered = false;
12417     this.rendered = false;
12418     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
12419     this.expanded = attributes.expanded === true;
12420     this.isTarget = attributes.isTarget !== false;
12421     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
12422     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
12423
12424     /**
12425      * Read-only. The text for this node. To change it use setText().
12426      * @type String
12427      */
12428     this.text = attributes.text;
12429     /**
12430      * True if this node is disabled.
12431      * @type Boolean
12432      */
12433     this.disabled = attributes.disabled === true;
12434
12435     this.addEvents({
12436         /**
12437         * @event textchange
12438         * Fires when the text for this node is changed
12439         * @param {Node} this This node
12440         * @param {String} text The new text
12441         * @param {String} oldText The old text
12442         */
12443         "textchange" : true,
12444         /**
12445         * @event beforeexpand
12446         * Fires before this node is expanded, return false to cancel.
12447         * @param {Node} this This node
12448         * @param {Boolean} deep
12449         * @param {Boolean} anim
12450         */
12451         "beforeexpand" : true,
12452         /**
12453         * @event beforecollapse
12454         * Fires before this node is collapsed, return false to cancel.
12455         * @param {Node} this This node
12456         * @param {Boolean} deep
12457         * @param {Boolean} anim
12458         */
12459         "beforecollapse" : true,
12460         /**
12461         * @event expand
12462         * Fires when this node is expanded
12463         * @param {Node} this This node
12464         */
12465         "expand" : true,
12466         /**
12467         * @event disabledchange
12468         * Fires when the disabled status of this node changes
12469         * @param {Node} this This node
12470         * @param {Boolean} disabled
12471         */
12472         "disabledchange" : true,
12473         /**
12474         * @event collapse
12475         * Fires when this node is collapsed
12476         * @param {Node} this This node
12477         */
12478         "collapse" : true,
12479         /**
12480         * @event beforeclick
12481         * Fires before click processing. Return false to cancel the default action.
12482         * @param {Node} this This node
12483         * @param {Roo.EventObject} e The event object
12484         */
12485         "beforeclick":true,
12486         /**
12487         * @event checkchange
12488         * Fires when a node with a checkbox's checked property changes
12489         * @param {Node} this This node
12490         * @param {Boolean} checked
12491         */
12492         "checkchange":true,
12493         /**
12494         * @event click
12495         * Fires when this node is clicked
12496         * @param {Node} this This node
12497         * @param {Roo.EventObject} e The event object
12498         */
12499         "click":true,
12500         /**
12501         * @event dblclick
12502         * Fires when this node is double clicked
12503         * @param {Node} this This node
12504         * @param {Roo.EventObject} e The event object
12505         */
12506         "dblclick":true,
12507         /**
12508         * @event contextmenu
12509         * Fires when this node is right clicked
12510         * @param {Node} this This node
12511         * @param {Roo.EventObject} e The event object
12512         */
12513         "contextmenu":true,
12514         /**
12515         * @event beforechildrenrendered
12516         * Fires right before the child nodes for this node are rendered
12517         * @param {Node} this This node
12518         */
12519         "beforechildrenrendered":true
12520     });
12521
12522     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
12523
12524     /**
12525      * Read-only. The UI for this node
12526      * @type TreeNodeUI
12527      */
12528     this.ui = new uiClass(this);
12529     
12530     // finally support items[]
12531     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
12532         return;
12533     }
12534     
12535     
12536     Roo.each(this.attributes.items, function(c) {
12537         this.appendChild(Roo.factory(c,Roo.Tree));
12538     }, this);
12539     delete this.attributes.items;
12540     
12541     
12542     
12543 };
12544 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
12545     preventHScroll: true,
12546     /**
12547      * Returns true if this node is expanded
12548      * @return {Boolean}
12549      */
12550     isExpanded : function(){
12551         return this.expanded;
12552     },
12553
12554     /**
12555      * Returns the UI object for this node
12556      * @return {TreeNodeUI}
12557      */
12558     getUI : function(){
12559         return this.ui;
12560     },
12561
12562     // private override
12563     setFirstChild : function(node){
12564         var of = this.firstChild;
12565         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
12566         if(this.childrenRendered && of && node != of){
12567             of.renderIndent(true, true);
12568         }
12569         if(this.rendered){
12570             this.renderIndent(true, true);
12571         }
12572     },
12573
12574     // private override
12575     setLastChild : function(node){
12576         var ol = this.lastChild;
12577         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
12578         if(this.childrenRendered && ol && node != ol){
12579             ol.renderIndent(true, true);
12580         }
12581         if(this.rendered){
12582             this.renderIndent(true, true);
12583         }
12584     },
12585
12586     // these methods are overridden to provide lazy rendering support
12587     // private override
12588     appendChild : function()
12589     {
12590         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
12591         if(node && this.childrenRendered){
12592             node.render();
12593         }
12594         this.ui.updateExpandIcon();
12595         return node;
12596     },
12597
12598     // private override
12599     removeChild : function(node){
12600         this.ownerTree.getSelectionModel().unselect(node);
12601         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
12602         // if it's been rendered remove dom node
12603         if(this.childrenRendered){
12604             node.ui.remove();
12605         }
12606         if(this.childNodes.length < 1){
12607             this.collapse(false, false);
12608         }else{
12609             this.ui.updateExpandIcon();
12610         }
12611         if(!this.firstChild) {
12612             this.childrenRendered = false;
12613         }
12614         return node;
12615     },
12616
12617     // private override
12618     insertBefore : function(node, refNode){
12619         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
12620         if(newNode && refNode && this.childrenRendered){
12621             node.render();
12622         }
12623         this.ui.updateExpandIcon();
12624         return newNode;
12625     },
12626
12627     /**
12628      * Sets the text for this node
12629      * @param {String} text
12630      */
12631     setText : function(text){
12632         var oldText = this.text;
12633         this.text = text;
12634         this.attributes.text = text;
12635         if(this.rendered){ // event without subscribing
12636             this.ui.onTextChange(this, text, oldText);
12637         }
12638         this.fireEvent("textchange", this, text, oldText);
12639     },
12640
12641     /**
12642      * Triggers selection of this node
12643      */
12644     select : function(){
12645         this.getOwnerTree().getSelectionModel().select(this);
12646     },
12647
12648     /**
12649      * Triggers deselection of this node
12650      */
12651     unselect : function(){
12652         this.getOwnerTree().getSelectionModel().unselect(this);
12653     },
12654
12655     /**
12656      * Returns true if this node is selected
12657      * @return {Boolean}
12658      */
12659     isSelected : function(){
12660         return this.getOwnerTree().getSelectionModel().isSelected(this);
12661     },
12662
12663     /**
12664      * Expand this node.
12665      * @param {Boolean} deep (optional) True to expand all children as well
12666      * @param {Boolean} anim (optional) false to cancel the default animation
12667      * @param {Function} callback (optional) A callback to be called when
12668      * expanding this node completes (does not wait for deep expand to complete).
12669      * Called with 1 parameter, this node.
12670      */
12671     expand : function(deep, anim, callback){
12672         if(!this.expanded){
12673             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
12674                 return;
12675             }
12676             if(!this.childrenRendered){
12677                 this.renderChildren();
12678             }
12679             this.expanded = true;
12680             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
12681                 this.ui.animExpand(function(){
12682                     this.fireEvent("expand", this);
12683                     if(typeof callback == "function"){
12684                         callback(this);
12685                     }
12686                     if(deep === true){
12687                         this.expandChildNodes(true);
12688                     }
12689                 }.createDelegate(this));
12690                 return;
12691             }else{
12692                 this.ui.expand();
12693                 this.fireEvent("expand", this);
12694                 if(typeof callback == "function"){
12695                     callback(this);
12696                 }
12697             }
12698         }else{
12699            if(typeof callback == "function"){
12700                callback(this);
12701            }
12702         }
12703         if(deep === true){
12704             this.expandChildNodes(true);
12705         }
12706     },
12707
12708     isHiddenRoot : function(){
12709         return this.isRoot && !this.getOwnerTree().rootVisible;
12710     },
12711
12712     /**
12713      * Collapse this node.
12714      * @param {Boolean} deep (optional) True to collapse all children as well
12715      * @param {Boolean} anim (optional) false to cancel the default animation
12716      */
12717     collapse : function(deep, anim){
12718         if(this.expanded && !this.isHiddenRoot()){
12719             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
12720                 return;
12721             }
12722             this.expanded = false;
12723             if((this.getOwnerTree().animate && anim !== false) || anim){
12724                 this.ui.animCollapse(function(){
12725                     this.fireEvent("collapse", this);
12726                     if(deep === true){
12727                         this.collapseChildNodes(true);
12728                     }
12729                 }.createDelegate(this));
12730                 return;
12731             }else{
12732                 this.ui.collapse();
12733                 this.fireEvent("collapse", this);
12734             }
12735         }
12736         if(deep === true){
12737             var cs = this.childNodes;
12738             for(var i = 0, len = cs.length; i < len; i++) {
12739                 cs[i].collapse(true, false);
12740             }
12741         }
12742     },
12743
12744     // private
12745     delayedExpand : function(delay){
12746         if(!this.expandProcId){
12747             this.expandProcId = this.expand.defer(delay, this);
12748         }
12749     },
12750
12751     // private
12752     cancelExpand : function(){
12753         if(this.expandProcId){
12754             clearTimeout(this.expandProcId);
12755         }
12756         this.expandProcId = false;
12757     },
12758
12759     /**
12760      * Toggles expanded/collapsed state of the node
12761      */
12762     toggle : function(){
12763         if(this.expanded){
12764             this.collapse();
12765         }else{
12766             this.expand();
12767         }
12768     },
12769
12770     /**
12771      * Ensures all parent nodes are expanded
12772      */
12773     ensureVisible : function(callback){
12774         var tree = this.getOwnerTree();
12775         tree.expandPath(this.parentNode.getPath(), false, function(){
12776             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
12777             Roo.callback(callback);
12778         }.createDelegate(this));
12779     },
12780
12781     /**
12782      * Expand all child nodes
12783      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
12784      */
12785     expandChildNodes : function(deep){
12786         var cs = this.childNodes;
12787         for(var i = 0, len = cs.length; i < len; i++) {
12788                 cs[i].expand(deep);
12789         }
12790     },
12791
12792     /**
12793      * Collapse all child nodes
12794      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
12795      */
12796     collapseChildNodes : function(deep){
12797         var cs = this.childNodes;
12798         for(var i = 0, len = cs.length; i < len; i++) {
12799                 cs[i].collapse(deep);
12800         }
12801     },
12802
12803     /**
12804      * Disables this node
12805      */
12806     disable : function(){
12807         this.disabled = true;
12808         this.unselect();
12809         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12810             this.ui.onDisableChange(this, true);
12811         }
12812         this.fireEvent("disabledchange", this, true);
12813     },
12814
12815     /**
12816      * Enables this node
12817      */
12818     enable : function(){
12819         this.disabled = false;
12820         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12821             this.ui.onDisableChange(this, false);
12822         }
12823         this.fireEvent("disabledchange", this, false);
12824     },
12825
12826     // private
12827     renderChildren : function(suppressEvent){
12828         if(suppressEvent !== false){
12829             this.fireEvent("beforechildrenrendered", this);
12830         }
12831         var cs = this.childNodes;
12832         for(var i = 0, len = cs.length; i < len; i++){
12833             cs[i].render(true);
12834         }
12835         this.childrenRendered = true;
12836     },
12837
12838     // private
12839     sort : function(fn, scope){
12840         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
12841         if(this.childrenRendered){
12842             var cs = this.childNodes;
12843             for(var i = 0, len = cs.length; i < len; i++){
12844                 cs[i].render(true);
12845             }
12846         }
12847     },
12848
12849     // private
12850     render : function(bulkRender){
12851         this.ui.render(bulkRender);
12852         if(!this.rendered){
12853             this.rendered = true;
12854             if(this.expanded){
12855                 this.expanded = false;
12856                 this.expand(false, false);
12857             }
12858         }
12859     },
12860
12861     // private
12862     renderIndent : function(deep, refresh){
12863         if(refresh){
12864             this.ui.childIndent = null;
12865         }
12866         this.ui.renderIndent();
12867         if(deep === true && this.childrenRendered){
12868             var cs = this.childNodes;
12869             for(var i = 0, len = cs.length; i < len; i++){
12870                 cs[i].renderIndent(true, refresh);
12871             }
12872         }
12873     }
12874 });/*
12875  * Based on:
12876  * Ext JS Library 1.1.1
12877  * Copyright(c) 2006-2007, Ext JS, LLC.
12878  *
12879  * Originally Released Under LGPL - original licence link has changed is not relivant.
12880  *
12881  * Fork - LGPL
12882  * <script type="text/javascript">
12883  */
12884  
12885 /**
12886  * @class Roo.tree.AsyncTreeNode
12887  * @extends Roo.tree.TreeNode
12888  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
12889  * @constructor
12890  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
12891  */
12892  Roo.tree.AsyncTreeNode = function(config){
12893     this.loaded = false;
12894     this.loading = false;
12895     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
12896     /**
12897     * @event beforeload
12898     * Fires before this node is loaded, return false to cancel
12899     * @param {Node} this This node
12900     */
12901     this.addEvents({'beforeload':true, 'load': true});
12902     /**
12903     * @event load
12904     * Fires when this node is loaded
12905     * @param {Node} this This node
12906     */
12907     /**
12908      * The loader used by this node (defaults to using the tree's defined loader)
12909      * @type TreeLoader
12910      * @property loader
12911      */
12912 };
12913 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
12914     expand : function(deep, anim, callback){
12915         if(this.loading){ // if an async load is already running, waiting til it's done
12916             var timer;
12917             var f = function(){
12918                 if(!this.loading){ // done loading
12919                     clearInterval(timer);
12920                     this.expand(deep, anim, callback);
12921                 }
12922             }.createDelegate(this);
12923             timer = setInterval(f, 200);
12924             return;
12925         }
12926         if(!this.loaded){
12927             if(this.fireEvent("beforeload", this) === false){
12928                 return;
12929             }
12930             this.loading = true;
12931             this.ui.beforeLoad(this);
12932             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
12933             if(loader){
12934                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
12935                 return;
12936             }
12937         }
12938         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
12939     },
12940     
12941     /**
12942      * Returns true if this node is currently loading
12943      * @return {Boolean}
12944      */
12945     isLoading : function(){
12946         return this.loading;  
12947     },
12948     
12949     loadComplete : function(deep, anim, callback){
12950         this.loading = false;
12951         this.loaded = true;
12952         this.ui.afterLoad(this);
12953         this.fireEvent("load", this);
12954         this.expand(deep, anim, callback);
12955     },
12956     
12957     /**
12958      * Returns true if this node has been loaded
12959      * @return {Boolean}
12960      */
12961     isLoaded : function(){
12962         return this.loaded;
12963     },
12964     
12965     hasChildNodes : function(){
12966         if(!this.isLeaf() && !this.loaded){
12967             return true;
12968         }else{
12969             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
12970         }
12971     },
12972
12973     /**
12974      * Trigger a reload for this node
12975      * @param {Function} callback
12976      */
12977     reload : function(callback){
12978         this.collapse(false, false);
12979         while(this.firstChild){
12980             this.removeChild(this.firstChild);
12981         }
12982         this.childrenRendered = false;
12983         this.loaded = false;
12984         if(this.isHiddenRoot()){
12985             this.expanded = false;
12986         }
12987         this.expand(false, false, callback);
12988     }
12989 });/*
12990  * Based on:
12991  * Ext JS Library 1.1.1
12992  * Copyright(c) 2006-2007, Ext JS, LLC.
12993  *
12994  * Originally Released Under LGPL - original licence link has changed is not relivant.
12995  *
12996  * Fork - LGPL
12997  * <script type="text/javascript">
12998  */
12999  
13000 /**
13001  * @class Roo.tree.TreeNodeUI
13002  * @constructor
13003  * @param {Object} node The node to render
13004  * The TreeNode UI implementation is separate from the
13005  * tree implementation. Unless you are customizing the tree UI,
13006  * you should never have to use this directly.
13007  */
13008 Roo.tree.TreeNodeUI = function(node){
13009     this.node = node;
13010     this.rendered = false;
13011     this.animating = false;
13012     this.emptyIcon = Roo.BLANK_IMAGE_URL;
13013 };
13014
13015 Roo.tree.TreeNodeUI.prototype = {
13016     removeChild : function(node){
13017         if(this.rendered){
13018             this.ctNode.removeChild(node.ui.getEl());
13019         }
13020     },
13021
13022     beforeLoad : function(){
13023          this.addClass("x-tree-node-loading");
13024     },
13025
13026     afterLoad : function(){
13027          this.removeClass("x-tree-node-loading");
13028     },
13029
13030     onTextChange : function(node, text, oldText){
13031         if(this.rendered){
13032             this.textNode.innerHTML = text;
13033         }
13034     },
13035
13036     onDisableChange : function(node, state){
13037         this.disabled = state;
13038         if(state){
13039             this.addClass("x-tree-node-disabled");
13040         }else{
13041             this.removeClass("x-tree-node-disabled");
13042         }
13043     },
13044
13045     onSelectedChange : function(state){
13046         if(state){
13047             this.focus();
13048             this.addClass("x-tree-selected");
13049         }else{
13050             //this.blur();
13051             this.removeClass("x-tree-selected");
13052         }
13053     },
13054
13055     onMove : function(tree, node, oldParent, newParent, index, refNode){
13056         this.childIndent = null;
13057         if(this.rendered){
13058             var targetNode = newParent.ui.getContainer();
13059             if(!targetNode){//target not rendered
13060                 this.holder = document.createElement("div");
13061                 this.holder.appendChild(this.wrap);
13062                 return;
13063             }
13064             var insertBefore = refNode ? refNode.ui.getEl() : null;
13065             if(insertBefore){
13066                 targetNode.insertBefore(this.wrap, insertBefore);
13067             }else{
13068                 targetNode.appendChild(this.wrap);
13069             }
13070             this.node.renderIndent(true);
13071         }
13072     },
13073
13074     addClass : function(cls){
13075         if(this.elNode){
13076             Roo.fly(this.elNode).addClass(cls);
13077         }
13078     },
13079
13080     removeClass : function(cls){
13081         if(this.elNode){
13082             Roo.fly(this.elNode).removeClass(cls);
13083         }
13084     },
13085
13086     remove : function(){
13087         if(this.rendered){
13088             this.holder = document.createElement("div");
13089             this.holder.appendChild(this.wrap);
13090         }
13091     },
13092
13093     fireEvent : function(){
13094         return this.node.fireEvent.apply(this.node, arguments);
13095     },
13096
13097     initEvents : function(){
13098         this.node.on("move", this.onMove, this);
13099         var E = Roo.EventManager;
13100         var a = this.anchor;
13101
13102         var el = Roo.fly(a, '_treeui');
13103
13104         if(Roo.isOpera){ // opera render bug ignores the CSS
13105             el.setStyle("text-decoration", "none");
13106         }
13107
13108         el.on("click", this.onClick, this);
13109         el.on("dblclick", this.onDblClick, this);
13110
13111         if(this.checkbox){
13112             Roo.EventManager.on(this.checkbox,
13113                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
13114         }
13115
13116         el.on("contextmenu", this.onContextMenu, this);
13117
13118         var icon = Roo.fly(this.iconNode);
13119         icon.on("click", this.onClick, this);
13120         icon.on("dblclick", this.onDblClick, this);
13121         icon.on("contextmenu", this.onContextMenu, this);
13122         E.on(this.ecNode, "click", this.ecClick, this, true);
13123
13124         if(this.node.disabled){
13125             this.addClass("x-tree-node-disabled");
13126         }
13127         if(this.node.hidden){
13128             this.addClass("x-tree-node-disabled");
13129         }
13130         var ot = this.node.getOwnerTree();
13131         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
13132         if(dd && (!this.node.isRoot || ot.rootVisible)){
13133             Roo.dd.Registry.register(this.elNode, {
13134                 node: this.node,
13135                 handles: this.getDDHandles(),
13136                 isHandle: false
13137             });
13138         }
13139     },
13140
13141     getDDHandles : function(){
13142         return [this.iconNode, this.textNode];
13143     },
13144
13145     hide : function(){
13146         if(this.rendered){
13147             this.wrap.style.display = "none";
13148         }
13149     },
13150
13151     show : function(){
13152         if(this.rendered){
13153             this.wrap.style.display = "";
13154         }
13155     },
13156
13157     onContextMenu : function(e){
13158         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
13159             e.preventDefault();
13160             this.focus();
13161             this.fireEvent("contextmenu", this.node, e);
13162         }
13163     },
13164
13165     onClick : function(e){
13166         if(this.dropping){
13167             e.stopEvent();
13168             return;
13169         }
13170         if(this.fireEvent("beforeclick", this.node, e) !== false){
13171             if(!this.disabled && this.node.attributes.href){
13172                 this.fireEvent("click", this.node, e);
13173                 return;
13174             }
13175             e.preventDefault();
13176             if(this.disabled){
13177                 return;
13178             }
13179
13180             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
13181                 this.node.toggle();
13182             }
13183
13184             this.fireEvent("click", this.node, e);
13185         }else{
13186             e.stopEvent();
13187         }
13188     },
13189
13190     onDblClick : function(e){
13191         e.preventDefault();
13192         if(this.disabled){
13193             return;
13194         }
13195         if(this.checkbox){
13196             this.toggleCheck();
13197         }
13198         if(!this.animating && this.node.hasChildNodes()){
13199             this.node.toggle();
13200         }
13201         this.fireEvent("dblclick", this.node, e);
13202     },
13203
13204     onCheckChange : function(){
13205         var checked = this.checkbox.checked;
13206         this.node.attributes.checked = checked;
13207         this.fireEvent('checkchange', this.node, checked);
13208     },
13209
13210     ecClick : function(e){
13211         if(!this.animating && this.node.hasChildNodes()){
13212             this.node.toggle();
13213         }
13214     },
13215
13216     startDrop : function(){
13217         this.dropping = true;
13218     },
13219
13220     // delayed drop so the click event doesn't get fired on a drop
13221     endDrop : function(){
13222        setTimeout(function(){
13223            this.dropping = false;
13224        }.createDelegate(this), 50);
13225     },
13226
13227     expand : function(){
13228         this.updateExpandIcon();
13229         this.ctNode.style.display = "";
13230     },
13231
13232     focus : function(){
13233         if(!this.node.preventHScroll){
13234             try{this.anchor.focus();
13235             }catch(e){}
13236         }else if(!Roo.isIE){
13237             try{
13238                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
13239                 var l = noscroll.scrollLeft;
13240                 this.anchor.focus();
13241                 noscroll.scrollLeft = l;
13242             }catch(e){}
13243         }
13244     },
13245
13246     toggleCheck : function(value){
13247         var cb = this.checkbox;
13248         if(cb){
13249             cb.checked = (value === undefined ? !cb.checked : value);
13250         }
13251     },
13252
13253     blur : function(){
13254         try{
13255             this.anchor.blur();
13256         }catch(e){}
13257     },
13258
13259     animExpand : function(callback){
13260         var ct = Roo.get(this.ctNode);
13261         ct.stopFx();
13262         if(!this.node.hasChildNodes()){
13263             this.updateExpandIcon();
13264             this.ctNode.style.display = "";
13265             Roo.callback(callback);
13266             return;
13267         }
13268         this.animating = true;
13269         this.updateExpandIcon();
13270
13271         ct.slideIn('t', {
13272            callback : function(){
13273                this.animating = false;
13274                Roo.callback(callback);
13275             },
13276             scope: this,
13277             duration: this.node.ownerTree.duration || .25
13278         });
13279     },
13280
13281     highlight : function(){
13282         var tree = this.node.getOwnerTree();
13283         Roo.fly(this.wrap).highlight(
13284             tree.hlColor || "C3DAF9",
13285             {endColor: tree.hlBaseColor}
13286         );
13287     },
13288
13289     collapse : function(){
13290         this.updateExpandIcon();
13291         this.ctNode.style.display = "none";
13292     },
13293
13294     animCollapse : function(callback){
13295         var ct = Roo.get(this.ctNode);
13296         ct.enableDisplayMode('block');
13297         ct.stopFx();
13298
13299         this.animating = true;
13300         this.updateExpandIcon();
13301
13302         ct.slideOut('t', {
13303             callback : function(){
13304                this.animating = false;
13305                Roo.callback(callback);
13306             },
13307             scope: this,
13308             duration: this.node.ownerTree.duration || .25
13309         });
13310     },
13311
13312     getContainer : function(){
13313         return this.ctNode;
13314     },
13315
13316     getEl : function(){
13317         return this.wrap;
13318     },
13319
13320     appendDDGhost : function(ghostNode){
13321         ghostNode.appendChild(this.elNode.cloneNode(true));
13322     },
13323
13324     getDDRepairXY : function(){
13325         return Roo.lib.Dom.getXY(this.iconNode);
13326     },
13327
13328     onRender : function(){
13329         this.render();
13330     },
13331
13332     render : function(bulkRender){
13333         var n = this.node, a = n.attributes;
13334         var targetNode = n.parentNode ?
13335               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
13336
13337         if(!this.rendered){
13338             this.rendered = true;
13339
13340             this.renderElements(n, a, targetNode, bulkRender);
13341
13342             if(a.qtip){
13343                if(this.textNode.setAttributeNS){
13344                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
13345                    if(a.qtipTitle){
13346                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
13347                    }
13348                }else{
13349                    this.textNode.setAttribute("ext:qtip", a.qtip);
13350                    if(a.qtipTitle){
13351                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
13352                    }
13353                }
13354             }else if(a.qtipCfg){
13355                 a.qtipCfg.target = Roo.id(this.textNode);
13356                 Roo.QuickTips.register(a.qtipCfg);
13357             }
13358             this.initEvents();
13359             if(!this.node.expanded){
13360                 this.updateExpandIcon();
13361             }
13362         }else{
13363             if(bulkRender === true) {
13364                 targetNode.appendChild(this.wrap);
13365             }
13366         }
13367     },
13368
13369     renderElements : function(n, a, targetNode, bulkRender)
13370     {
13371         // add some indent caching, this helps performance when rendering a large tree
13372         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
13373         var t = n.getOwnerTree();
13374         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
13375         if (typeof(n.attributes.html) != 'undefined') {
13376             txt = n.attributes.html;
13377         }
13378         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
13379         var cb = typeof a.checked == 'boolean';
13380         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
13381         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
13382             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
13383             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
13384             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
13385             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
13386             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
13387              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
13388                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
13389             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
13390             "</li>"];
13391
13392         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
13393             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
13394                                 n.nextSibling.ui.getEl(), buf.join(""));
13395         }else{
13396             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
13397         }
13398
13399         this.elNode = this.wrap.childNodes[0];
13400         this.ctNode = this.wrap.childNodes[1];
13401         var cs = this.elNode.childNodes;
13402         this.indentNode = cs[0];
13403         this.ecNode = cs[1];
13404         this.iconNode = cs[2];
13405         var index = 3;
13406         if(cb){
13407             this.checkbox = cs[3];
13408             index++;
13409         }
13410         this.anchor = cs[index];
13411         this.textNode = cs[index].firstChild;
13412     },
13413
13414     getAnchor : function(){
13415         return this.anchor;
13416     },
13417
13418     getTextEl : function(){
13419         return this.textNode;
13420     },
13421
13422     getIconEl : function(){
13423         return this.iconNode;
13424     },
13425
13426     isChecked : function(){
13427         return this.checkbox ? this.checkbox.checked : false;
13428     },
13429
13430     updateExpandIcon : function(){
13431         if(this.rendered){
13432             var n = this.node, c1, c2;
13433             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
13434             var hasChild = n.hasChildNodes();
13435             if(hasChild){
13436                 if(n.expanded){
13437                     cls += "-minus";
13438                     c1 = "x-tree-node-collapsed";
13439                     c2 = "x-tree-node-expanded";
13440                 }else{
13441                     cls += "-plus";
13442                     c1 = "x-tree-node-expanded";
13443                     c2 = "x-tree-node-collapsed";
13444                 }
13445                 if(this.wasLeaf){
13446                     this.removeClass("x-tree-node-leaf");
13447                     this.wasLeaf = false;
13448                 }
13449                 if(this.c1 != c1 || this.c2 != c2){
13450                     Roo.fly(this.elNode).replaceClass(c1, c2);
13451                     this.c1 = c1; this.c2 = c2;
13452                 }
13453             }else{
13454                 // this changes non-leafs into leafs if they have no children.
13455                 // it's not very rational behaviour..
13456                 
13457                 if(!this.wasLeaf && this.node.leaf){
13458                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
13459                     delete this.c1;
13460                     delete this.c2;
13461                     this.wasLeaf = true;
13462                 }
13463             }
13464             var ecc = "x-tree-ec-icon "+cls;
13465             if(this.ecc != ecc){
13466                 this.ecNode.className = ecc;
13467                 this.ecc = ecc;
13468             }
13469         }
13470     },
13471
13472     getChildIndent : function(){
13473         if(!this.childIndent){
13474             var buf = [];
13475             var p = this.node;
13476             while(p){
13477                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
13478                     if(!p.isLast()) {
13479                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
13480                     } else {
13481                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
13482                     }
13483                 }
13484                 p = p.parentNode;
13485             }
13486             this.childIndent = buf.join("");
13487         }
13488         return this.childIndent;
13489     },
13490
13491     renderIndent : function(){
13492         if(this.rendered){
13493             var indent = "";
13494             var p = this.node.parentNode;
13495             if(p){
13496                 indent = p.ui.getChildIndent();
13497             }
13498             if(this.indentMarkup != indent){ // don't rerender if not required
13499                 this.indentNode.innerHTML = indent;
13500                 this.indentMarkup = indent;
13501             }
13502             this.updateExpandIcon();
13503         }
13504     }
13505 };
13506
13507 Roo.tree.RootTreeNodeUI = function(){
13508     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
13509 };
13510 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
13511     render : function(){
13512         if(!this.rendered){
13513             var targetNode = this.node.ownerTree.innerCt.dom;
13514             this.node.expanded = true;
13515             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
13516             this.wrap = this.ctNode = targetNode.firstChild;
13517         }
13518     },
13519     collapse : function(){
13520     },
13521     expand : function(){
13522     }
13523 });/*
13524  * Based on:
13525  * Ext JS Library 1.1.1
13526  * Copyright(c) 2006-2007, Ext JS, LLC.
13527  *
13528  * Originally Released Under LGPL - original licence link has changed is not relivant.
13529  *
13530  * Fork - LGPL
13531  * <script type="text/javascript">
13532  */
13533 /**
13534  * @class Roo.tree.TreeLoader
13535  * @extends Roo.util.Observable
13536  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
13537  * nodes from a specified URL. The response must be a javascript Array definition
13538  * who's elements are node definition objects. eg:
13539  * <pre><code>
13540 {  success : true,
13541    data :      [
13542    
13543     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
13544     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
13545     ]
13546 }
13547
13548
13549 </code></pre>
13550  * <br><br>
13551  * The old style respose with just an array is still supported, but not recommended.
13552  * <br><br>
13553  *
13554  * A server request is sent, and child nodes are loaded only when a node is expanded.
13555  * The loading node's id is passed to the server under the parameter name "node" to
13556  * enable the server to produce the correct child nodes.
13557  * <br><br>
13558  * To pass extra parameters, an event handler may be attached to the "beforeload"
13559  * event, and the parameters specified in the TreeLoader's baseParams property:
13560  * <pre><code>
13561     myTreeLoader.on("beforeload", function(treeLoader, node) {
13562         this.baseParams.category = node.attributes.category;
13563     }, this);
13564 </code></pre><
13565  * This would pass an HTTP parameter called "category" to the server containing
13566  * the value of the Node's "category" attribute.
13567  * @constructor
13568  * Creates a new Treeloader.
13569  * @param {Object} config A config object containing config properties.
13570  */
13571 Roo.tree.TreeLoader = function(config){
13572     this.baseParams = {};
13573     this.requestMethod = "POST";
13574     Roo.apply(this, config);
13575
13576     this.addEvents({
13577     
13578         /**
13579          * @event beforeload
13580          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
13581          * @param {Object} This TreeLoader object.
13582          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13583          * @param {Object} callback The callback function specified in the {@link #load} call.
13584          */
13585         beforeload : true,
13586         /**
13587          * @event load
13588          * Fires when the node has been successfuly loaded.
13589          * @param {Object} This TreeLoader object.
13590          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13591          * @param {Object} response The response object containing the data from the server.
13592          */
13593         load : true,
13594         /**
13595          * @event loadexception
13596          * Fires if the network request failed.
13597          * @param {Object} This TreeLoader object.
13598          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13599          * @param {Object} response The response object containing the data from the server.
13600          */
13601         loadexception : true,
13602         /**
13603          * @event create
13604          * Fires before a node is created, enabling you to return custom Node types 
13605          * @param {Object} This TreeLoader object.
13606          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
13607          */
13608         create : true
13609     });
13610
13611     Roo.tree.TreeLoader.superclass.constructor.call(this);
13612 };
13613
13614 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
13615     /**
13616     * @cfg {String} dataUrl The URL from which to request a Json string which
13617     * specifies an array of node definition object representing the child nodes
13618     * to be loaded.
13619     */
13620     /**
13621     * @cfg {String} requestMethod either GET or POST
13622     * defaults to POST (due to BC)
13623     * to be loaded.
13624     */
13625     /**
13626     * @cfg {Object} baseParams (optional) An object containing properties which
13627     * specify HTTP parameters to be passed to each request for child nodes.
13628     */
13629     /**
13630     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
13631     * created by this loader. If the attributes sent by the server have an attribute in this object,
13632     * they take priority.
13633     */
13634     /**
13635     * @cfg {Object} uiProviders (optional) An object containing properties which
13636     * 
13637     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
13638     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
13639     * <i>uiProvider</i> attribute of a returned child node is a string rather
13640     * than a reference to a TreeNodeUI implementation, this that string value
13641     * is used as a property name in the uiProviders object. You can define the provider named
13642     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
13643     */
13644     uiProviders : {},
13645
13646     /**
13647     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
13648     * child nodes before loading.
13649     */
13650     clearOnLoad : true,
13651
13652     /**
13653     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
13654     * property on loading, rather than expecting an array. (eg. more compatible to a standard
13655     * Grid query { data : [ .....] }
13656     */
13657     
13658     root : false,
13659      /**
13660     * @cfg {String} queryParam (optional) 
13661     * Name of the query as it will be passed on the querystring (defaults to 'node')
13662     * eg. the request will be ?node=[id]
13663     */
13664     
13665     
13666     queryParam: false,
13667     
13668     /**
13669      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
13670      * This is called automatically when a node is expanded, but may be used to reload
13671      * a node (or append new children if the {@link #clearOnLoad} option is false.)
13672      * @param {Roo.tree.TreeNode} node
13673      * @param {Function} callback
13674      */
13675     load : function(node, callback){
13676         if(this.clearOnLoad){
13677             while(node.firstChild){
13678                 node.removeChild(node.firstChild);
13679             }
13680         }
13681         if(node.attributes.children){ // preloaded json children
13682             var cs = node.attributes.children;
13683             for(var i = 0, len = cs.length; i < len; i++){
13684                 node.appendChild(this.createNode(cs[i]));
13685             }
13686             if(typeof callback == "function"){
13687                 callback();
13688             }
13689         }else if(this.dataUrl){
13690             this.requestData(node, callback);
13691         }
13692     },
13693
13694     getParams: function(node){
13695         var buf = [], bp = this.baseParams;
13696         for(var key in bp){
13697             if(typeof bp[key] != "function"){
13698                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
13699             }
13700         }
13701         var n = this.queryParam === false ? 'node' : this.queryParam;
13702         buf.push(n + "=", encodeURIComponent(node.id));
13703         return buf.join("");
13704     },
13705
13706     requestData : function(node, callback){
13707         if(this.fireEvent("beforeload", this, node, callback) !== false){
13708             this.transId = Roo.Ajax.request({
13709                 method:this.requestMethod,
13710                 url: this.dataUrl||this.url,
13711                 success: this.handleResponse,
13712                 failure: this.handleFailure,
13713                 scope: this,
13714                 argument: {callback: callback, node: node},
13715                 params: this.getParams(node)
13716             });
13717         }else{
13718             // if the load is cancelled, make sure we notify
13719             // the node that we are done
13720             if(typeof callback == "function"){
13721                 callback();
13722             }
13723         }
13724     },
13725
13726     isLoading : function(){
13727         return this.transId ? true : false;
13728     },
13729
13730     abort : function(){
13731         if(this.isLoading()){
13732             Roo.Ajax.abort(this.transId);
13733         }
13734     },
13735
13736     // private
13737     createNode : function(attr)
13738     {
13739         // apply baseAttrs, nice idea Corey!
13740         if(this.baseAttrs){
13741             Roo.applyIf(attr, this.baseAttrs);
13742         }
13743         if(this.applyLoader !== false){
13744             attr.loader = this;
13745         }
13746         // uiProvider = depreciated..
13747         
13748         if(typeof(attr.uiProvider) == 'string'){
13749            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
13750                 /**  eval:var:attr */ eval(attr.uiProvider);
13751         }
13752         if(typeof(this.uiProviders['default']) != 'undefined') {
13753             attr.uiProvider = this.uiProviders['default'];
13754         }
13755         
13756         this.fireEvent('create', this, attr);
13757         
13758         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
13759         return(attr.leaf ?
13760                         new Roo.tree.TreeNode(attr) :
13761                         new Roo.tree.AsyncTreeNode(attr));
13762     },
13763
13764     processResponse : function(response, node, callback)
13765     {
13766         var json = response.responseText;
13767         try {
13768             
13769             var o = Roo.decode(json);
13770             
13771             if (this.root === false && typeof(o.success) != undefined) {
13772                 this.root = 'data'; // the default behaviour for list like data..
13773                 }
13774                 
13775             if (this.root !== false &&  !o.success) {
13776                 // it's a failure condition.
13777                 var a = response.argument;
13778                 this.fireEvent("loadexception", this, a.node, response);
13779                 Roo.log("Load failed - should have a handler really");
13780                 return;
13781             }
13782             
13783             
13784             
13785             if (this.root !== false) {
13786                  o = o[this.root];
13787             }
13788             
13789             for(var i = 0, len = o.length; i < len; i++){
13790                 var n = this.createNode(o[i]);
13791                 if(n){
13792                     node.appendChild(n);
13793                 }
13794             }
13795             if(typeof callback == "function"){
13796                 callback(this, node);
13797             }
13798         }catch(e){
13799             this.handleFailure(response);
13800         }
13801     },
13802
13803     handleResponse : function(response){
13804         this.transId = false;
13805         var a = response.argument;
13806         this.processResponse(response, a.node, a.callback);
13807         this.fireEvent("load", this, a.node, response);
13808     },
13809
13810     handleFailure : function(response)
13811     {
13812         // should handle failure better..
13813         this.transId = false;
13814         var a = response.argument;
13815         this.fireEvent("loadexception", this, a.node, response);
13816         if(typeof a.callback == "function"){
13817             a.callback(this, a.node);
13818         }
13819     }
13820 });/*
13821  * Based on:
13822  * Ext JS Library 1.1.1
13823  * Copyright(c) 2006-2007, Ext JS, LLC.
13824  *
13825  * Originally Released Under LGPL - original licence link has changed is not relivant.
13826  *
13827  * Fork - LGPL
13828  * <script type="text/javascript">
13829  */
13830
13831 /**
13832 * @class Roo.tree.TreeFilter
13833 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
13834 * @param {TreePanel} tree
13835 * @param {Object} config (optional)
13836  */
13837 Roo.tree.TreeFilter = function(tree, config){
13838     this.tree = tree;
13839     this.filtered = {};
13840     Roo.apply(this, config);
13841 };
13842
13843 Roo.tree.TreeFilter.prototype = {
13844     clearBlank:false,
13845     reverse:false,
13846     autoClear:false,
13847     remove:false,
13848
13849      /**
13850      * Filter the data by a specific attribute.
13851      * @param {String/RegExp} value Either string that the attribute value
13852      * should start with or a RegExp to test against the attribute
13853      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
13854      * @param {TreeNode} startNode (optional) The node to start the filter at.
13855      */
13856     filter : function(value, attr, startNode){
13857         attr = attr || "text";
13858         var f;
13859         if(typeof value == "string"){
13860             var vlen = value.length;
13861             // auto clear empty filter
13862             if(vlen == 0 && this.clearBlank){
13863                 this.clear();
13864                 return;
13865             }
13866             value = value.toLowerCase();
13867             f = function(n){
13868                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
13869             };
13870         }else if(value.exec){ // regex?
13871             f = function(n){
13872                 return value.test(n.attributes[attr]);
13873             };
13874         }else{
13875             throw 'Illegal filter type, must be string or regex';
13876         }
13877         this.filterBy(f, null, startNode);
13878         },
13879
13880     /**
13881      * Filter by a function. The passed function will be called with each
13882      * node in the tree (or from the startNode). If the function returns true, the node is kept
13883      * otherwise it is filtered. If a node is filtered, its children are also filtered.
13884      * @param {Function} fn The filter function
13885      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
13886      */
13887     filterBy : function(fn, scope, startNode){
13888         startNode = startNode || this.tree.root;
13889         if(this.autoClear){
13890             this.clear();
13891         }
13892         var af = this.filtered, rv = this.reverse;
13893         var f = function(n){
13894             if(n == startNode){
13895                 return true;
13896             }
13897             if(af[n.id]){
13898                 return false;
13899             }
13900             var m = fn.call(scope || n, n);
13901             if(!m || rv){
13902                 af[n.id] = n;
13903                 n.ui.hide();
13904                 return false;
13905             }
13906             return true;
13907         };
13908         startNode.cascade(f);
13909         if(this.remove){
13910            for(var id in af){
13911                if(typeof id != "function"){
13912                    var n = af[id];
13913                    if(n && n.parentNode){
13914                        n.parentNode.removeChild(n);
13915                    }
13916                }
13917            }
13918         }
13919     },
13920
13921     /**
13922      * Clears the current filter. Note: with the "remove" option
13923      * set a filter cannot be cleared.
13924      */
13925     clear : function(){
13926         var t = this.tree;
13927         var af = this.filtered;
13928         for(var id in af){
13929             if(typeof id != "function"){
13930                 var n = af[id];
13931                 if(n){
13932                     n.ui.show();
13933                 }
13934             }
13935         }
13936         this.filtered = {};
13937     }
13938 };
13939 /*
13940  * Based on:
13941  * Ext JS Library 1.1.1
13942  * Copyright(c) 2006-2007, Ext JS, LLC.
13943  *
13944  * Originally Released Under LGPL - original licence link has changed is not relivant.
13945  *
13946  * Fork - LGPL
13947  * <script type="text/javascript">
13948  */
13949  
13950
13951 /**
13952  * @class Roo.tree.TreeSorter
13953  * Provides sorting of nodes in a TreePanel
13954  * 
13955  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
13956  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
13957  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
13958  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
13959  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
13960  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
13961  * @constructor
13962  * @param {TreePanel} tree
13963  * @param {Object} config
13964  */
13965 Roo.tree.TreeSorter = function(tree, config){
13966     Roo.apply(this, config);
13967     tree.on("beforechildrenrendered", this.doSort, this);
13968     tree.on("append", this.updateSort, this);
13969     tree.on("insert", this.updateSort, this);
13970     
13971     var dsc = this.dir && this.dir.toLowerCase() == "desc";
13972     var p = this.property || "text";
13973     var sortType = this.sortType;
13974     var fs = this.folderSort;
13975     var cs = this.caseSensitive === true;
13976     var leafAttr = this.leafAttr || 'leaf';
13977
13978     this.sortFn = function(n1, n2){
13979         if(fs){
13980             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
13981                 return 1;
13982             }
13983             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
13984                 return -1;
13985             }
13986         }
13987         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
13988         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
13989         if(v1 < v2){
13990                         return dsc ? +1 : -1;
13991                 }else if(v1 > v2){
13992                         return dsc ? -1 : +1;
13993         }else{
13994                 return 0;
13995         }
13996     };
13997 };
13998
13999 Roo.tree.TreeSorter.prototype = {
14000     doSort : function(node){
14001         node.sort(this.sortFn);
14002     },
14003     
14004     compareNodes : function(n1, n2){
14005         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
14006     },
14007     
14008     updateSort : function(tree, node){
14009         if(node.childrenRendered){
14010             this.doSort.defer(1, this, [node]);
14011         }
14012     }
14013 };/*
14014  * Based on:
14015  * Ext JS Library 1.1.1
14016  * Copyright(c) 2006-2007, Ext JS, LLC.
14017  *
14018  * Originally Released Under LGPL - original licence link has changed is not relivant.
14019  *
14020  * Fork - LGPL
14021  * <script type="text/javascript">
14022  */
14023
14024 if(Roo.dd.DropZone){
14025     
14026 Roo.tree.TreeDropZone = function(tree, config){
14027     this.allowParentInsert = false;
14028     this.allowContainerDrop = false;
14029     this.appendOnly = false;
14030     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
14031     this.tree = tree;
14032     this.lastInsertClass = "x-tree-no-status";
14033     this.dragOverData = {};
14034 };
14035
14036 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
14037     ddGroup : "TreeDD",
14038     scroll:  true,
14039     
14040     expandDelay : 1000,
14041     
14042     expandNode : function(node){
14043         if(node.hasChildNodes() && !node.isExpanded()){
14044             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
14045         }
14046     },
14047     
14048     queueExpand : function(node){
14049         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
14050     },
14051     
14052     cancelExpand : function(){
14053         if(this.expandProcId){
14054             clearTimeout(this.expandProcId);
14055             this.expandProcId = false;
14056         }
14057     },
14058     
14059     isValidDropPoint : function(n, pt, dd, e, data){
14060         if(!n || !data){ return false; }
14061         var targetNode = n.node;
14062         var dropNode = data.node;
14063         // default drop rules
14064         if(!(targetNode && targetNode.isTarget && pt)){
14065             return false;
14066         }
14067         if(pt == "append" && targetNode.allowChildren === false){
14068             return false;
14069         }
14070         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
14071             return false;
14072         }
14073         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
14074             return false;
14075         }
14076         // reuse the object
14077         var overEvent = this.dragOverData;
14078         overEvent.tree = this.tree;
14079         overEvent.target = targetNode;
14080         overEvent.data = data;
14081         overEvent.point = pt;
14082         overEvent.source = dd;
14083         overEvent.rawEvent = e;
14084         overEvent.dropNode = dropNode;
14085         overEvent.cancel = false;  
14086         var result = this.tree.fireEvent("nodedragover", overEvent);
14087         return overEvent.cancel === false && result !== false;
14088     },
14089     
14090     getDropPoint : function(e, n, dd)
14091     {
14092         var tn = n.node;
14093         if(tn.isRoot){
14094             return tn.allowChildren !== false ? "append" : false; // always append for root
14095         }
14096         var dragEl = n.ddel;
14097         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
14098         var y = Roo.lib.Event.getPageY(e);
14099         //var noAppend = tn.allowChildren === false || tn.isLeaf();
14100         
14101         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
14102         var noAppend = tn.allowChildren === false;
14103         if(this.appendOnly || tn.parentNode.allowChildren === false){
14104             return noAppend ? false : "append";
14105         }
14106         var noBelow = false;
14107         if(!this.allowParentInsert){
14108             noBelow = tn.hasChildNodes() && tn.isExpanded();
14109         }
14110         var q = (b - t) / (noAppend ? 2 : 3);
14111         if(y >= t && y < (t + q)){
14112             return "above";
14113         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
14114             return "below";
14115         }else{
14116             return "append";
14117         }
14118     },
14119     
14120     onNodeEnter : function(n, dd, e, data)
14121     {
14122         this.cancelExpand();
14123     },
14124     
14125     onNodeOver : function(n, dd, e, data)
14126     {
14127        
14128         var pt = this.getDropPoint(e, n, dd);
14129         var node = n.node;
14130         
14131         // auto node expand check
14132         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
14133             this.queueExpand(node);
14134         }else if(pt != "append"){
14135             this.cancelExpand();
14136         }
14137         
14138         // set the insert point style on the target node
14139         var returnCls = this.dropNotAllowed;
14140         if(this.isValidDropPoint(n, pt, dd, e, data)){
14141            if(pt){
14142                var el = n.ddel;
14143                var cls;
14144                if(pt == "above"){
14145                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
14146                    cls = "x-tree-drag-insert-above";
14147                }else if(pt == "below"){
14148                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
14149                    cls = "x-tree-drag-insert-below";
14150                }else{
14151                    returnCls = "x-tree-drop-ok-append";
14152                    cls = "x-tree-drag-append";
14153                }
14154                if(this.lastInsertClass != cls){
14155                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
14156                    this.lastInsertClass = cls;
14157                }
14158            }
14159        }
14160        return returnCls;
14161     },
14162     
14163     onNodeOut : function(n, dd, e, data){
14164         
14165         this.cancelExpand();
14166         this.removeDropIndicators(n);
14167     },
14168     
14169     onNodeDrop : function(n, dd, e, data){
14170         var point = this.getDropPoint(e, n, dd);
14171         var targetNode = n.node;
14172         targetNode.ui.startDrop();
14173         if(!this.isValidDropPoint(n, point, dd, e, data)){
14174             targetNode.ui.endDrop();
14175             return false;
14176         }
14177         // first try to find the drop node
14178         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
14179         var dropEvent = {
14180             tree : this.tree,
14181             target: targetNode,
14182             data: data,
14183             point: point,
14184             source: dd,
14185             rawEvent: e,
14186             dropNode: dropNode,
14187             cancel: !dropNode   
14188         };
14189         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
14190         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
14191             targetNode.ui.endDrop();
14192             return false;
14193         }
14194         // allow target changing
14195         targetNode = dropEvent.target;
14196         if(point == "append" && !targetNode.isExpanded()){
14197             targetNode.expand(false, null, function(){
14198                 this.completeDrop(dropEvent);
14199             }.createDelegate(this));
14200         }else{
14201             this.completeDrop(dropEvent);
14202         }
14203         return true;
14204     },
14205     
14206     completeDrop : function(de){
14207         var ns = de.dropNode, p = de.point, t = de.target;
14208         if(!(ns instanceof Array)){
14209             ns = [ns];
14210         }
14211         var n;
14212         for(var i = 0, len = ns.length; i < len; i++){
14213             n = ns[i];
14214             if(p == "above"){
14215                 t.parentNode.insertBefore(n, t);
14216             }else if(p == "below"){
14217                 t.parentNode.insertBefore(n, t.nextSibling);
14218             }else{
14219                 t.appendChild(n);
14220             }
14221         }
14222         n.ui.focus();
14223         if(this.tree.hlDrop){
14224             n.ui.highlight();
14225         }
14226         t.ui.endDrop();
14227         this.tree.fireEvent("nodedrop", de);
14228     },
14229     
14230     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
14231         if(this.tree.hlDrop){
14232             dropNode.ui.focus();
14233             dropNode.ui.highlight();
14234         }
14235         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
14236     },
14237     
14238     getTree : function(){
14239         return this.tree;
14240     },
14241     
14242     removeDropIndicators : function(n){
14243         if(n && n.ddel){
14244             var el = n.ddel;
14245             Roo.fly(el).removeClass([
14246                     "x-tree-drag-insert-above",
14247                     "x-tree-drag-insert-below",
14248                     "x-tree-drag-append"]);
14249             this.lastInsertClass = "_noclass";
14250         }
14251     },
14252     
14253     beforeDragDrop : function(target, e, id){
14254         this.cancelExpand();
14255         return true;
14256     },
14257     
14258     afterRepair : function(data){
14259         if(data && Roo.enableFx){
14260             data.node.ui.highlight();
14261         }
14262         this.hideProxy();
14263     } 
14264     
14265 });
14266
14267 }
14268 /*
14269  * Based on:
14270  * Ext JS Library 1.1.1
14271  * Copyright(c) 2006-2007, Ext JS, LLC.
14272  *
14273  * Originally Released Under LGPL - original licence link has changed is not relivant.
14274  *
14275  * Fork - LGPL
14276  * <script type="text/javascript">
14277  */
14278  
14279
14280 if(Roo.dd.DragZone){
14281 Roo.tree.TreeDragZone = function(tree, config){
14282     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
14283     this.tree = tree;
14284 };
14285
14286 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
14287     ddGroup : "TreeDD",
14288    
14289     onBeforeDrag : function(data, e){
14290         var n = data.node;
14291         return n && n.draggable && !n.disabled;
14292     },
14293      
14294     
14295     onInitDrag : function(e){
14296         var data = this.dragData;
14297         this.tree.getSelectionModel().select(data.node);
14298         this.proxy.update("");
14299         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
14300         this.tree.fireEvent("startdrag", this.tree, data.node, e);
14301     },
14302     
14303     getRepairXY : function(e, data){
14304         return data.node.ui.getDDRepairXY();
14305     },
14306     
14307     onEndDrag : function(data, e){
14308         this.tree.fireEvent("enddrag", this.tree, data.node, e);
14309         
14310         
14311     },
14312     
14313     onValidDrop : function(dd, e, id){
14314         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
14315         this.hideProxy();
14316     },
14317     
14318     beforeInvalidDrop : function(e, id){
14319         // this scrolls the original position back into view
14320         var sm = this.tree.getSelectionModel();
14321         sm.clearSelections();
14322         sm.select(this.dragData.node);
14323     }
14324 });
14325 }/*
14326  * Based on:
14327  * Ext JS Library 1.1.1
14328  * Copyright(c) 2006-2007, Ext JS, LLC.
14329  *
14330  * Originally Released Under LGPL - original licence link has changed is not relivant.
14331  *
14332  * Fork - LGPL
14333  * <script type="text/javascript">
14334  */
14335 /**
14336  * @class Roo.tree.TreeEditor
14337  * @extends Roo.Editor
14338  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
14339  * as the editor field.
14340  * @constructor
14341  * @param {Object} config (used to be the tree panel.)
14342  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
14343  * 
14344  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
14345  * @cfg {Roo.form.TextField|Object} field The field configuration
14346  *
14347  * 
14348  */
14349 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
14350     var tree = config;
14351     var field;
14352     if (oldconfig) { // old style..
14353         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
14354     } else {
14355         // new style..
14356         tree = config.tree;
14357         config.field = config.field  || {};
14358         config.field.xtype = 'TextField';
14359         field = Roo.factory(config.field, Roo.form);
14360     }
14361     config = config || {};
14362     
14363     
14364     this.addEvents({
14365         /**
14366          * @event beforenodeedit
14367          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
14368          * false from the handler of this event.
14369          * @param {Editor} this
14370          * @param {Roo.tree.Node} node 
14371          */
14372         "beforenodeedit" : true
14373     });
14374     
14375     //Roo.log(config);
14376     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
14377
14378     this.tree = tree;
14379
14380     tree.on('beforeclick', this.beforeNodeClick, this);
14381     tree.getTreeEl().on('mousedown', this.hide, this);
14382     this.on('complete', this.updateNode, this);
14383     this.on('beforestartedit', this.fitToTree, this);
14384     this.on('startedit', this.bindScroll, this, {delay:10});
14385     this.on('specialkey', this.onSpecialKey, this);
14386 };
14387
14388 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
14389     /**
14390      * @cfg {String} alignment
14391      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
14392      */
14393     alignment: "l-l",
14394     // inherit
14395     autoSize: false,
14396     /**
14397      * @cfg {Boolean} hideEl
14398      * True to hide the bound element while the editor is displayed (defaults to false)
14399      */
14400     hideEl : false,
14401     /**
14402      * @cfg {String} cls
14403      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
14404      */
14405     cls: "x-small-editor x-tree-editor",
14406     /**
14407      * @cfg {Boolean} shim
14408      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
14409      */
14410     shim:false,
14411     // inherit
14412     shadow:"frame",
14413     /**
14414      * @cfg {Number} maxWidth
14415      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
14416      * the containing tree element's size, it will be automatically limited for you to the container width, taking
14417      * scroll and client offsets into account prior to each edit.
14418      */
14419     maxWidth: 250,
14420
14421     editDelay : 350,
14422
14423     // private
14424     fitToTree : function(ed, el){
14425         var td = this.tree.getTreeEl().dom, nd = el.dom;
14426         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
14427             td.scrollLeft = nd.offsetLeft;
14428         }
14429         var w = Math.min(
14430                 this.maxWidth,
14431                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
14432         this.setSize(w, '');
14433         
14434         return this.fireEvent('beforenodeedit', this, this.editNode);
14435         
14436     },
14437
14438     // private
14439     triggerEdit : function(node){
14440         this.completeEdit();
14441         this.editNode = node;
14442         this.startEdit(node.ui.textNode, node.text);
14443     },
14444
14445     // private
14446     bindScroll : function(){
14447         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
14448     },
14449
14450     // private
14451     beforeNodeClick : function(node, e){
14452         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
14453         this.lastClick = new Date();
14454         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
14455             e.stopEvent();
14456             this.triggerEdit(node);
14457             return false;
14458         }
14459         return true;
14460     },
14461
14462     // private
14463     updateNode : function(ed, value){
14464         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
14465         this.editNode.setText(value);
14466     },
14467
14468     // private
14469     onHide : function(){
14470         Roo.tree.TreeEditor.superclass.onHide.call(this);
14471         if(this.editNode){
14472             this.editNode.ui.focus();
14473         }
14474     },
14475
14476     // private
14477     onSpecialKey : function(field, e){
14478         var k = e.getKey();
14479         if(k == e.ESC){
14480             e.stopEvent();
14481             this.cancelEdit();
14482         }else if(k == e.ENTER && !e.hasModifier()){
14483             e.stopEvent();
14484             this.completeEdit();
14485         }
14486     }
14487 });//<Script type="text/javascript">
14488 /*
14489  * Based on:
14490  * Ext JS Library 1.1.1
14491  * Copyright(c) 2006-2007, Ext JS, LLC.
14492  *
14493  * Originally Released Under LGPL - original licence link has changed is not relivant.
14494  *
14495  * Fork - LGPL
14496  * <script type="text/javascript">
14497  */
14498  
14499 /**
14500  * Not documented??? - probably should be...
14501  */
14502
14503 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
14504     //focus: Roo.emptyFn, // prevent odd scrolling behavior
14505     
14506     renderElements : function(n, a, targetNode, bulkRender){
14507         //consel.log("renderElements?");
14508         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
14509
14510         var t = n.getOwnerTree();
14511         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
14512         
14513         var cols = t.columns;
14514         var bw = t.borderWidth;
14515         var c = cols[0];
14516         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
14517          var cb = typeof a.checked == "boolean";
14518         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14519         var colcls = 'x-t-' + tid + '-c0';
14520         var buf = [
14521             '<li class="x-tree-node">',
14522             
14523                 
14524                 '<div class="x-tree-node-el ', a.cls,'">',
14525                     // extran...
14526                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
14527                 
14528                 
14529                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
14530                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
14531                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
14532                            (a.icon ? ' x-tree-node-inline-icon' : ''),
14533                            (a.iconCls ? ' '+a.iconCls : ''),
14534                            '" unselectable="on" />',
14535                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
14536                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
14537                              
14538                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14539                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
14540                             '<span unselectable="on" qtip="' + tx + '">',
14541                              tx,
14542                              '</span></a>' ,
14543                     '</div>',
14544                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14545                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
14546                  ];
14547         for(var i = 1, len = cols.length; i < len; i++){
14548             c = cols[i];
14549             colcls = 'x-t-' + tid + '-c' +i;
14550             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14551             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
14552                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
14553                       "</div>");
14554          }
14555          
14556          buf.push(
14557             '</a>',
14558             '<div class="x-clear"></div></div>',
14559             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
14560             "</li>");
14561         
14562         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
14563             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
14564                                 n.nextSibling.ui.getEl(), buf.join(""));
14565         }else{
14566             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
14567         }
14568         var el = this.wrap.firstChild;
14569         this.elRow = el;
14570         this.elNode = el.firstChild;
14571         this.ranchor = el.childNodes[1];
14572         this.ctNode = this.wrap.childNodes[1];
14573         var cs = el.firstChild.childNodes;
14574         this.indentNode = cs[0];
14575         this.ecNode = cs[1];
14576         this.iconNode = cs[2];
14577         var index = 3;
14578         if(cb){
14579             this.checkbox = cs[3];
14580             index++;
14581         }
14582         this.anchor = cs[index];
14583         
14584         this.textNode = cs[index].firstChild;
14585         
14586         //el.on("click", this.onClick, this);
14587         //el.on("dblclick", this.onDblClick, this);
14588         
14589         
14590        // console.log(this);
14591     },
14592     initEvents : function(){
14593         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
14594         
14595             
14596         var a = this.ranchor;
14597
14598         var el = Roo.get(a);
14599
14600         if(Roo.isOpera){ // opera render bug ignores the CSS
14601             el.setStyle("text-decoration", "none");
14602         }
14603
14604         el.on("click", this.onClick, this);
14605         el.on("dblclick", this.onDblClick, this);
14606         el.on("contextmenu", this.onContextMenu, this);
14607         
14608     },
14609     
14610     /*onSelectedChange : function(state){
14611         if(state){
14612             this.focus();
14613             this.addClass("x-tree-selected");
14614         }else{
14615             //this.blur();
14616             this.removeClass("x-tree-selected");
14617         }
14618     },*/
14619     addClass : function(cls){
14620         if(this.elRow){
14621             Roo.fly(this.elRow).addClass(cls);
14622         }
14623         
14624     },
14625     
14626     
14627     removeClass : function(cls){
14628         if(this.elRow){
14629             Roo.fly(this.elRow).removeClass(cls);
14630         }
14631     }
14632
14633     
14634     
14635 });//<Script type="text/javascript">
14636
14637 /*
14638  * Based on:
14639  * Ext JS Library 1.1.1
14640  * Copyright(c) 2006-2007, Ext JS, LLC.
14641  *
14642  * Originally Released Under LGPL - original licence link has changed is not relivant.
14643  *
14644  * Fork - LGPL
14645  * <script type="text/javascript">
14646  */
14647  
14648
14649 /**
14650  * @class Roo.tree.ColumnTree
14651  * @extends Roo.data.TreePanel
14652  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
14653  * @cfg {int} borderWidth  compined right/left border allowance
14654  * @constructor
14655  * @param {String/HTMLElement/Element} el The container element
14656  * @param {Object} config
14657  */
14658 Roo.tree.ColumnTree =  function(el, config)
14659 {
14660    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
14661    this.addEvents({
14662         /**
14663         * @event resize
14664         * Fire this event on a container when it resizes
14665         * @param {int} w Width
14666         * @param {int} h Height
14667         */
14668        "resize" : true
14669     });
14670     this.on('resize', this.onResize, this);
14671 };
14672
14673 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
14674     //lines:false,
14675     
14676     
14677     borderWidth: Roo.isBorderBox ? 0 : 2, 
14678     headEls : false,
14679     
14680     render : function(){
14681         // add the header.....
14682        
14683         Roo.tree.ColumnTree.superclass.render.apply(this);
14684         
14685         this.el.addClass('x-column-tree');
14686         
14687         this.headers = this.el.createChild(
14688             {cls:'x-tree-headers'},this.innerCt.dom);
14689    
14690         var cols = this.columns, c;
14691         var totalWidth = 0;
14692         this.headEls = [];
14693         var  len = cols.length;
14694         for(var i = 0; i < len; i++){
14695              c = cols[i];
14696              totalWidth += c.width;
14697             this.headEls.push(this.headers.createChild({
14698                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
14699                  cn: {
14700                      cls:'x-tree-hd-text',
14701                      html: c.header
14702                  },
14703                  style:'width:'+(c.width-this.borderWidth)+'px;'
14704              }));
14705         }
14706         this.headers.createChild({cls:'x-clear'});
14707         // prevent floats from wrapping when clipped
14708         this.headers.setWidth(totalWidth);
14709         //this.innerCt.setWidth(totalWidth);
14710         this.innerCt.setStyle({ overflow: 'auto' });
14711         this.onResize(this.width, this.height);
14712              
14713         
14714     },
14715     onResize : function(w,h)
14716     {
14717         this.height = h;
14718         this.width = w;
14719         // resize cols..
14720         this.innerCt.setWidth(this.width);
14721         this.innerCt.setHeight(this.height-20);
14722         
14723         // headers...
14724         var cols = this.columns, c;
14725         var totalWidth = 0;
14726         var expEl = false;
14727         var len = cols.length;
14728         for(var i = 0; i < len; i++){
14729             c = cols[i];
14730             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
14731                 // it's the expander..
14732                 expEl  = this.headEls[i];
14733                 continue;
14734             }
14735             totalWidth += c.width;
14736             
14737         }
14738         if (expEl) {
14739             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
14740         }
14741         this.headers.setWidth(w-20);
14742
14743         
14744         
14745         
14746     }
14747 });
14748 /*
14749  * Based on:
14750  * Ext JS Library 1.1.1
14751  * Copyright(c) 2006-2007, Ext JS, LLC.
14752  *
14753  * Originally Released Under LGPL - original licence link has changed is not relivant.
14754  *
14755  * Fork - LGPL
14756  * <script type="text/javascript">
14757  */
14758  
14759 /**
14760  * @class Roo.menu.Menu
14761  * @extends Roo.util.Observable
14762  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
14763  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
14764  * @constructor
14765  * Creates a new Menu
14766  * @param {Object} config Configuration options
14767  */
14768 Roo.menu.Menu = function(config){
14769     Roo.apply(this, config);
14770     this.id = this.id || Roo.id();
14771     this.addEvents({
14772         /**
14773          * @event beforeshow
14774          * Fires before this menu is displayed
14775          * @param {Roo.menu.Menu} this
14776          */
14777         beforeshow : true,
14778         /**
14779          * @event beforehide
14780          * Fires before this menu is hidden
14781          * @param {Roo.menu.Menu} this
14782          */
14783         beforehide : true,
14784         /**
14785          * @event show
14786          * Fires after this menu is displayed
14787          * @param {Roo.menu.Menu} this
14788          */
14789         show : true,
14790         /**
14791          * @event hide
14792          * Fires after this menu is hidden
14793          * @param {Roo.menu.Menu} this
14794          */
14795         hide : true,
14796         /**
14797          * @event click
14798          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
14799          * @param {Roo.menu.Menu} this
14800          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14801          * @param {Roo.EventObject} e
14802          */
14803         click : true,
14804         /**
14805          * @event mouseover
14806          * Fires when the mouse is hovering over this menu
14807          * @param {Roo.menu.Menu} this
14808          * @param {Roo.EventObject} e
14809          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14810          */
14811         mouseover : true,
14812         /**
14813          * @event mouseout
14814          * Fires when the mouse exits this menu
14815          * @param {Roo.menu.Menu} this
14816          * @param {Roo.EventObject} e
14817          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14818          */
14819         mouseout : true,
14820         /**
14821          * @event itemclick
14822          * Fires when a menu item contained in this menu is clicked
14823          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
14824          * @param {Roo.EventObject} e
14825          */
14826         itemclick: true
14827     });
14828     if (this.registerMenu) {
14829         Roo.menu.MenuMgr.register(this);
14830     }
14831     
14832     var mis = this.items;
14833     this.items = new Roo.util.MixedCollection();
14834     if(mis){
14835         this.add.apply(this, mis);
14836     }
14837 };
14838
14839 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
14840     /**
14841      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
14842      */
14843     minWidth : 120,
14844     /**
14845      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
14846      * for bottom-right shadow (defaults to "sides")
14847      */
14848     shadow : "sides",
14849     /**
14850      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
14851      * this menu (defaults to "tl-tr?")
14852      */
14853     subMenuAlign : "tl-tr?",
14854     /**
14855      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
14856      * relative to its element of origin (defaults to "tl-bl?")
14857      */
14858     defaultAlign : "tl-bl?",
14859     /**
14860      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
14861      */
14862     allowOtherMenus : false,
14863     /**
14864      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
14865      */
14866     registerMenu : true,
14867
14868     hidden:true,
14869
14870     // private
14871     render : function(){
14872         if(this.el){
14873             return;
14874         }
14875         var el = this.el = new Roo.Layer({
14876             cls: "x-menu",
14877             shadow:this.shadow,
14878             constrain: false,
14879             parentEl: this.parentEl || document.body,
14880             zindex:15000
14881         });
14882
14883         this.keyNav = new Roo.menu.MenuNav(this);
14884
14885         if(this.plain){
14886             el.addClass("x-menu-plain");
14887         }
14888         if(this.cls){
14889             el.addClass(this.cls);
14890         }
14891         // generic focus element
14892         this.focusEl = el.createChild({
14893             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
14894         });
14895         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
14896         //disabling touch- as it's causing issues ..
14897         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
14898         ul.on('click'   , this.onClick, this);
14899         
14900         
14901         ul.on("mouseover", this.onMouseOver, this);
14902         ul.on("mouseout", this.onMouseOut, this);
14903         this.items.each(function(item){
14904             if (item.hidden) {
14905                 return;
14906             }
14907             
14908             var li = document.createElement("li");
14909             li.className = "x-menu-list-item";
14910             ul.dom.appendChild(li);
14911             item.render(li, this);
14912         }, this);
14913         this.ul = ul;
14914         this.autoWidth();
14915     },
14916
14917     // private
14918     autoWidth : function(){
14919         var el = this.el, ul = this.ul;
14920         if(!el){
14921             return;
14922         }
14923         var w = this.width;
14924         if(w){
14925             el.setWidth(w);
14926         }else if(Roo.isIE){
14927             el.setWidth(this.minWidth);
14928             var t = el.dom.offsetWidth; // force recalc
14929             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
14930         }
14931     },
14932
14933     // private
14934     delayAutoWidth : function(){
14935         if(this.rendered){
14936             if(!this.awTask){
14937                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
14938             }
14939             this.awTask.delay(20);
14940         }
14941     },
14942
14943     // private
14944     findTargetItem : function(e){
14945         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
14946         if(t && t.menuItemId){
14947             return this.items.get(t.menuItemId);
14948         }
14949     },
14950
14951     // private
14952     onClick : function(e){
14953         Roo.log("menu.onClick");
14954         var t = this.findTargetItem(e);
14955         if(!t){
14956             return;
14957         }
14958         Roo.log(e);
14959         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
14960             if(t == this.activeItem && t.shouldDeactivate(e)){
14961                 this.activeItem.deactivate();
14962                 delete this.activeItem;
14963                 return;
14964             }
14965             if(t.canActivate){
14966                 this.setActiveItem(t, true);
14967             }
14968             return;
14969             
14970             
14971         }
14972         
14973         t.onClick(e);
14974         this.fireEvent("click", this, t, e);
14975     },
14976
14977     // private
14978     setActiveItem : function(item, autoExpand){
14979         if(item != this.activeItem){
14980             if(this.activeItem){
14981                 this.activeItem.deactivate();
14982             }
14983             this.activeItem = item;
14984             item.activate(autoExpand);
14985         }else if(autoExpand){
14986             item.expandMenu();
14987         }
14988     },
14989
14990     // private
14991     tryActivate : function(start, step){
14992         var items = this.items;
14993         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
14994             var item = items.get(i);
14995             if(!item.disabled && item.canActivate){
14996                 this.setActiveItem(item, false);
14997                 return item;
14998             }
14999         }
15000         return false;
15001     },
15002
15003     // private
15004     onMouseOver : function(e){
15005         var t;
15006         if(t = this.findTargetItem(e)){
15007             if(t.canActivate && !t.disabled){
15008                 this.setActiveItem(t, true);
15009             }
15010         }
15011         this.fireEvent("mouseover", this, e, t);
15012     },
15013
15014     // private
15015     onMouseOut : function(e){
15016         var t;
15017         if(t = this.findTargetItem(e)){
15018             if(t == this.activeItem && t.shouldDeactivate(e)){
15019                 this.activeItem.deactivate();
15020                 delete this.activeItem;
15021             }
15022         }
15023         this.fireEvent("mouseout", this, e, t);
15024     },
15025
15026     /**
15027      * Read-only.  Returns true if the menu is currently displayed, else false.
15028      * @type Boolean
15029      */
15030     isVisible : function(){
15031         return this.el && !this.hidden;
15032     },
15033
15034     /**
15035      * Displays this menu relative to another element
15036      * @param {String/HTMLElement/Roo.Element} element The element to align to
15037      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
15038      * the element (defaults to this.defaultAlign)
15039      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
15040      */
15041     show : function(el, pos, parentMenu){
15042         this.parentMenu = parentMenu;
15043         if(!this.el){
15044             this.render();
15045         }
15046         this.fireEvent("beforeshow", this);
15047         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
15048     },
15049
15050     /**
15051      * Displays this menu at a specific xy position
15052      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
15053      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
15054      */
15055     showAt : function(xy, parentMenu, /* private: */_e){
15056         this.parentMenu = parentMenu;
15057         if(!this.el){
15058             this.render();
15059         }
15060         if(_e !== false){
15061             this.fireEvent("beforeshow", this);
15062             xy = this.el.adjustForConstraints(xy);
15063         }
15064         this.el.setXY(xy);
15065         this.el.show();
15066         this.hidden = false;
15067         this.focus();
15068         this.fireEvent("show", this);
15069     },
15070
15071     focus : function(){
15072         if(!this.hidden){
15073             this.doFocus.defer(50, this);
15074         }
15075     },
15076
15077     doFocus : function(){
15078         if(!this.hidden){
15079             this.focusEl.focus();
15080         }
15081     },
15082
15083     /**
15084      * Hides this menu and optionally all parent menus
15085      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
15086      */
15087     hide : function(deep){
15088         if(this.el && this.isVisible()){
15089             this.fireEvent("beforehide", this);
15090             if(this.activeItem){
15091                 this.activeItem.deactivate();
15092                 this.activeItem = null;
15093             }
15094             this.el.hide();
15095             this.hidden = true;
15096             this.fireEvent("hide", this);
15097         }
15098         if(deep === true && this.parentMenu){
15099             this.parentMenu.hide(true);
15100         }
15101     },
15102
15103     /**
15104      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
15105      * Any of the following are valid:
15106      * <ul>
15107      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
15108      * <li>An HTMLElement object which will be converted to a menu item</li>
15109      * <li>A menu item config object that will be created as a new menu item</li>
15110      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
15111      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
15112      * </ul>
15113      * Usage:
15114      * <pre><code>
15115 // Create the menu
15116 var menu = new Roo.menu.Menu();
15117
15118 // Create a menu item to add by reference
15119 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
15120
15121 // Add a bunch of items at once using different methods.
15122 // Only the last item added will be returned.
15123 var item = menu.add(
15124     menuItem,                // add existing item by ref
15125     'Dynamic Item',          // new TextItem
15126     '-',                     // new separator
15127     { text: 'Config Item' }  // new item by config
15128 );
15129 </code></pre>
15130      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
15131      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
15132      */
15133     add : function(){
15134         var a = arguments, l = a.length, item;
15135         for(var i = 0; i < l; i++){
15136             var el = a[i];
15137             if ((typeof(el) == "object") && el.xtype && el.xns) {
15138                 el = Roo.factory(el, Roo.menu);
15139             }
15140             
15141             if(el.render){ // some kind of Item
15142                 item = this.addItem(el);
15143             }else if(typeof el == "string"){ // string
15144                 if(el == "separator" || el == "-"){
15145                     item = this.addSeparator();
15146                 }else{
15147                     item = this.addText(el);
15148                 }
15149             }else if(el.tagName || el.el){ // element
15150                 item = this.addElement(el);
15151             }else if(typeof el == "object"){ // must be menu item config?
15152                 item = this.addMenuItem(el);
15153             }
15154         }
15155         return item;
15156     },
15157
15158     /**
15159      * Returns this menu's underlying {@link Roo.Element} object
15160      * @return {Roo.Element} The element
15161      */
15162     getEl : function(){
15163         if(!this.el){
15164             this.render();
15165         }
15166         return this.el;
15167     },
15168
15169     /**
15170      * Adds a separator bar to the menu
15171      * @return {Roo.menu.Item} The menu item that was added
15172      */
15173     addSeparator : function(){
15174         return this.addItem(new Roo.menu.Separator());
15175     },
15176
15177     /**
15178      * Adds an {@link Roo.Element} object to the menu
15179      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
15180      * @return {Roo.menu.Item} The menu item that was added
15181      */
15182     addElement : function(el){
15183         return this.addItem(new Roo.menu.BaseItem(el));
15184     },
15185
15186     /**
15187      * Adds an existing object based on {@link Roo.menu.Item} to the menu
15188      * @param {Roo.menu.Item} item The menu item to add
15189      * @return {Roo.menu.Item} The menu item that was added
15190      */
15191     addItem : function(item){
15192         this.items.add(item);
15193         if(this.ul){
15194             var li = document.createElement("li");
15195             li.className = "x-menu-list-item";
15196             this.ul.dom.appendChild(li);
15197             item.render(li, this);
15198             this.delayAutoWidth();
15199         }
15200         return item;
15201     },
15202
15203     /**
15204      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
15205      * @param {Object} config A MenuItem config object
15206      * @return {Roo.menu.Item} The menu item that was added
15207      */
15208     addMenuItem : function(config){
15209         if(!(config instanceof Roo.menu.Item)){
15210             if(typeof config.checked == "boolean"){ // must be check menu item config?
15211                 config = new Roo.menu.CheckItem(config);
15212             }else{
15213                 config = new Roo.menu.Item(config);
15214             }
15215         }
15216         return this.addItem(config);
15217     },
15218
15219     /**
15220      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
15221      * @param {String} text The text to display in the menu item
15222      * @return {Roo.menu.Item} The menu item that was added
15223      */
15224     addText : function(text){
15225         return this.addItem(new Roo.menu.TextItem({ text : text }));
15226     },
15227
15228     /**
15229      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
15230      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
15231      * @param {Roo.menu.Item} item The menu item to add
15232      * @return {Roo.menu.Item} The menu item that was added
15233      */
15234     insert : function(index, item){
15235         this.items.insert(index, item);
15236         if(this.ul){
15237             var li = document.createElement("li");
15238             li.className = "x-menu-list-item";
15239             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
15240             item.render(li, this);
15241             this.delayAutoWidth();
15242         }
15243         return item;
15244     },
15245
15246     /**
15247      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
15248      * @param {Roo.menu.Item} item The menu item to remove
15249      */
15250     remove : function(item){
15251         this.items.removeKey(item.id);
15252         item.destroy();
15253     },
15254
15255     /**
15256      * Removes and destroys all items in the menu
15257      */
15258     removeAll : function(){
15259         var f;
15260         while(f = this.items.first()){
15261             this.remove(f);
15262         }
15263     }
15264 });
15265
15266 // MenuNav is a private utility class used internally by the Menu
15267 Roo.menu.MenuNav = function(menu){
15268     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
15269     this.scope = this.menu = menu;
15270 };
15271
15272 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
15273     doRelay : function(e, h){
15274         var k = e.getKey();
15275         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
15276             this.menu.tryActivate(0, 1);
15277             return false;
15278         }
15279         return h.call(this.scope || this, e, this.menu);
15280     },
15281
15282     up : function(e, m){
15283         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
15284             m.tryActivate(m.items.length-1, -1);
15285         }
15286     },
15287
15288     down : function(e, m){
15289         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
15290             m.tryActivate(0, 1);
15291         }
15292     },
15293
15294     right : function(e, m){
15295         if(m.activeItem){
15296             m.activeItem.expandMenu(true);
15297         }
15298     },
15299
15300     left : function(e, m){
15301         m.hide();
15302         if(m.parentMenu && m.parentMenu.activeItem){
15303             m.parentMenu.activeItem.activate();
15304         }
15305     },
15306
15307     enter : function(e, m){
15308         if(m.activeItem){
15309             e.stopPropagation();
15310             m.activeItem.onClick(e);
15311             m.fireEvent("click", this, m.activeItem);
15312             return true;
15313         }
15314     }
15315 });/*
15316  * Based on:
15317  * Ext JS Library 1.1.1
15318  * Copyright(c) 2006-2007, Ext JS, LLC.
15319  *
15320  * Originally Released Under LGPL - original licence link has changed is not relivant.
15321  *
15322  * Fork - LGPL
15323  * <script type="text/javascript">
15324  */
15325  
15326 /**
15327  * @class Roo.menu.MenuMgr
15328  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
15329  * @singleton
15330  */
15331 Roo.menu.MenuMgr = function(){
15332    var menus, active, groups = {}, attached = false, lastShow = new Date();
15333
15334    // private - called when first menu is created
15335    function init(){
15336        menus = {};
15337        active = new Roo.util.MixedCollection();
15338        Roo.get(document).addKeyListener(27, function(){
15339            if(active.length > 0){
15340                hideAll();
15341            }
15342        });
15343    }
15344
15345    // private
15346    function hideAll(){
15347        if(active && active.length > 0){
15348            var c = active.clone();
15349            c.each(function(m){
15350                m.hide();
15351            });
15352        }
15353    }
15354
15355    // private
15356    function onHide(m){
15357        active.remove(m);
15358        if(active.length < 1){
15359            Roo.get(document).un("mousedown", onMouseDown);
15360            attached = false;
15361        }
15362    }
15363
15364    // private
15365    function onShow(m){
15366        var last = active.last();
15367        lastShow = new Date();
15368        active.add(m);
15369        if(!attached){
15370            Roo.get(document).on("mousedown", onMouseDown);
15371            attached = true;
15372        }
15373        if(m.parentMenu){
15374           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
15375           m.parentMenu.activeChild = m;
15376        }else if(last && last.isVisible()){
15377           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
15378        }
15379    }
15380
15381    // private
15382    function onBeforeHide(m){
15383        if(m.activeChild){
15384            m.activeChild.hide();
15385        }
15386        if(m.autoHideTimer){
15387            clearTimeout(m.autoHideTimer);
15388            delete m.autoHideTimer;
15389        }
15390    }
15391
15392    // private
15393    function onBeforeShow(m){
15394        var pm = m.parentMenu;
15395        if(!pm && !m.allowOtherMenus){
15396            hideAll();
15397        }else if(pm && pm.activeChild && active != m){
15398            pm.activeChild.hide();
15399        }
15400    }
15401
15402    // private
15403    function onMouseDown(e){
15404        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
15405            hideAll();
15406        }
15407    }
15408
15409    // private
15410    function onBeforeCheck(mi, state){
15411        if(state){
15412            var g = groups[mi.group];
15413            for(var i = 0, l = g.length; i < l; i++){
15414                if(g[i] != mi){
15415                    g[i].setChecked(false);
15416                }
15417            }
15418        }
15419    }
15420
15421    return {
15422
15423        /**
15424         * Hides all menus that are currently visible
15425         */
15426        hideAll : function(){
15427             hideAll();  
15428        },
15429
15430        // private
15431        register : function(menu){
15432            if(!menus){
15433                init();
15434            }
15435            menus[menu.id] = menu;
15436            menu.on("beforehide", onBeforeHide);
15437            menu.on("hide", onHide);
15438            menu.on("beforeshow", onBeforeShow);
15439            menu.on("show", onShow);
15440            var g = menu.group;
15441            if(g && menu.events["checkchange"]){
15442                if(!groups[g]){
15443                    groups[g] = [];
15444                }
15445                groups[g].push(menu);
15446                menu.on("checkchange", onCheck);
15447            }
15448        },
15449
15450         /**
15451          * Returns a {@link Roo.menu.Menu} object
15452          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
15453          * be used to generate and return a new Menu instance.
15454          */
15455        get : function(menu){
15456            if(typeof menu == "string"){ // menu id
15457                return menus[menu];
15458            }else if(menu.events){  // menu instance
15459                return menu;
15460            }else if(typeof menu.length == 'number'){ // array of menu items?
15461                return new Roo.menu.Menu({items:menu});
15462            }else{ // otherwise, must be a config
15463                return new Roo.menu.Menu(menu);
15464            }
15465        },
15466
15467        // private
15468        unregister : function(menu){
15469            delete menus[menu.id];
15470            menu.un("beforehide", onBeforeHide);
15471            menu.un("hide", onHide);
15472            menu.un("beforeshow", onBeforeShow);
15473            menu.un("show", onShow);
15474            var g = menu.group;
15475            if(g && menu.events["checkchange"]){
15476                groups[g].remove(menu);
15477                menu.un("checkchange", onCheck);
15478            }
15479        },
15480
15481        // private
15482        registerCheckable : function(menuItem){
15483            var g = menuItem.group;
15484            if(g){
15485                if(!groups[g]){
15486                    groups[g] = [];
15487                }
15488                groups[g].push(menuItem);
15489                menuItem.on("beforecheckchange", onBeforeCheck);
15490            }
15491        },
15492
15493        // private
15494        unregisterCheckable : function(menuItem){
15495            var g = menuItem.group;
15496            if(g){
15497                groups[g].remove(menuItem);
15498                menuItem.un("beforecheckchange", onBeforeCheck);
15499            }
15500        }
15501    };
15502 }();/*
15503  * Based on:
15504  * Ext JS Library 1.1.1
15505  * Copyright(c) 2006-2007, Ext JS, LLC.
15506  *
15507  * Originally Released Under LGPL - original licence link has changed is not relivant.
15508  *
15509  * Fork - LGPL
15510  * <script type="text/javascript">
15511  */
15512  
15513
15514 /**
15515  * @class Roo.menu.BaseItem
15516  * @extends Roo.Component
15517  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
15518  * management and base configuration options shared by all menu components.
15519  * @constructor
15520  * Creates a new BaseItem
15521  * @param {Object} config Configuration options
15522  */
15523 Roo.menu.BaseItem = function(config){
15524     Roo.menu.BaseItem.superclass.constructor.call(this, config);
15525
15526     this.addEvents({
15527         /**
15528          * @event click
15529          * Fires when this item is clicked
15530          * @param {Roo.menu.BaseItem} this
15531          * @param {Roo.EventObject} e
15532          */
15533         click: true,
15534         /**
15535          * @event activate
15536          * Fires when this item is activated
15537          * @param {Roo.menu.BaseItem} this
15538          */
15539         activate : true,
15540         /**
15541          * @event deactivate
15542          * Fires when this item is deactivated
15543          * @param {Roo.menu.BaseItem} this
15544          */
15545         deactivate : true
15546     });
15547
15548     if(this.handler){
15549         this.on("click", this.handler, this.scope, true);
15550     }
15551 };
15552
15553 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
15554     /**
15555      * @cfg {Function} handler
15556      * A function that will handle the click event of this menu item (defaults to undefined)
15557      */
15558     /**
15559      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
15560      */
15561     canActivate : false,
15562     
15563      /**
15564      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
15565      */
15566     hidden: false,
15567     
15568     /**
15569      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
15570      */
15571     activeClass : "x-menu-item-active",
15572     /**
15573      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
15574      */
15575     hideOnClick : true,
15576     /**
15577      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
15578      */
15579     hideDelay : 100,
15580
15581     // private
15582     ctype: "Roo.menu.BaseItem",
15583
15584     // private
15585     actionMode : "container",
15586
15587     // private
15588     render : function(container, parentMenu){
15589         this.parentMenu = parentMenu;
15590         Roo.menu.BaseItem.superclass.render.call(this, container);
15591         this.container.menuItemId = this.id;
15592     },
15593
15594     // private
15595     onRender : function(container, position){
15596         this.el = Roo.get(this.el);
15597         container.dom.appendChild(this.el.dom);
15598     },
15599
15600     // private
15601     onClick : function(e){
15602         if(!this.disabled && this.fireEvent("click", this, e) !== false
15603                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
15604             this.handleClick(e);
15605         }else{
15606             e.stopEvent();
15607         }
15608     },
15609
15610     // private
15611     activate : function(){
15612         if(this.disabled){
15613             return false;
15614         }
15615         var li = this.container;
15616         li.addClass(this.activeClass);
15617         this.region = li.getRegion().adjust(2, 2, -2, -2);
15618         this.fireEvent("activate", this);
15619         return true;
15620     },
15621
15622     // private
15623     deactivate : function(){
15624         this.container.removeClass(this.activeClass);
15625         this.fireEvent("deactivate", this);
15626     },
15627
15628     // private
15629     shouldDeactivate : function(e){
15630         return !this.region || !this.region.contains(e.getPoint());
15631     },
15632
15633     // private
15634     handleClick : function(e){
15635         if(this.hideOnClick){
15636             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
15637         }
15638     },
15639
15640     // private
15641     expandMenu : function(autoActivate){
15642         // do nothing
15643     },
15644
15645     // private
15646     hideMenu : function(){
15647         // do nothing
15648     }
15649 });/*
15650  * Based on:
15651  * Ext JS Library 1.1.1
15652  * Copyright(c) 2006-2007, Ext JS, LLC.
15653  *
15654  * Originally Released Under LGPL - original licence link has changed is not relivant.
15655  *
15656  * Fork - LGPL
15657  * <script type="text/javascript">
15658  */
15659  
15660 /**
15661  * @class Roo.menu.Adapter
15662  * @extends Roo.menu.BaseItem
15663  * 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.
15664  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
15665  * @constructor
15666  * Creates a new Adapter
15667  * @param {Object} config Configuration options
15668  */
15669 Roo.menu.Adapter = function(component, config){
15670     Roo.menu.Adapter.superclass.constructor.call(this, config);
15671     this.component = component;
15672 };
15673 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
15674     // private
15675     canActivate : true,
15676
15677     // private
15678     onRender : function(container, position){
15679         this.component.render(container);
15680         this.el = this.component.getEl();
15681     },
15682
15683     // private
15684     activate : function(){
15685         if(this.disabled){
15686             return false;
15687         }
15688         this.component.focus();
15689         this.fireEvent("activate", this);
15690         return true;
15691     },
15692
15693     // private
15694     deactivate : function(){
15695         this.fireEvent("deactivate", this);
15696     },
15697
15698     // private
15699     disable : function(){
15700         this.component.disable();
15701         Roo.menu.Adapter.superclass.disable.call(this);
15702     },
15703
15704     // private
15705     enable : function(){
15706         this.component.enable();
15707         Roo.menu.Adapter.superclass.enable.call(this);
15708     }
15709 });/*
15710  * Based on:
15711  * Ext JS Library 1.1.1
15712  * Copyright(c) 2006-2007, Ext JS, LLC.
15713  *
15714  * Originally Released Under LGPL - original licence link has changed is not relivant.
15715  *
15716  * Fork - LGPL
15717  * <script type="text/javascript">
15718  */
15719
15720 /**
15721  * @class Roo.menu.TextItem
15722  * @extends Roo.menu.BaseItem
15723  * Adds a static text string to a menu, usually used as either a heading or group separator.
15724  * Note: old style constructor with text is still supported.
15725  * 
15726  * @constructor
15727  * Creates a new TextItem
15728  * @param {Object} cfg Configuration
15729  */
15730 Roo.menu.TextItem = function(cfg){
15731     if (typeof(cfg) == 'string') {
15732         this.text = cfg;
15733     } else {
15734         Roo.apply(this,cfg);
15735     }
15736     
15737     Roo.menu.TextItem.superclass.constructor.call(this);
15738 };
15739
15740 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
15741     /**
15742      * @cfg {Boolean} text Text to show on item.
15743      */
15744     text : '',
15745     
15746     /**
15747      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15748      */
15749     hideOnClick : false,
15750     /**
15751      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
15752      */
15753     itemCls : "x-menu-text",
15754
15755     // private
15756     onRender : function(){
15757         var s = document.createElement("span");
15758         s.className = this.itemCls;
15759         s.innerHTML = this.text;
15760         this.el = s;
15761         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
15762     }
15763 });/*
15764  * Based on:
15765  * Ext JS Library 1.1.1
15766  * Copyright(c) 2006-2007, Ext JS, LLC.
15767  *
15768  * Originally Released Under LGPL - original licence link has changed is not relivant.
15769  *
15770  * Fork - LGPL
15771  * <script type="text/javascript">
15772  */
15773
15774 /**
15775  * @class Roo.menu.Separator
15776  * @extends Roo.menu.BaseItem
15777  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
15778  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
15779  * @constructor
15780  * @param {Object} config Configuration options
15781  */
15782 Roo.menu.Separator = function(config){
15783     Roo.menu.Separator.superclass.constructor.call(this, config);
15784 };
15785
15786 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
15787     /**
15788      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
15789      */
15790     itemCls : "x-menu-sep",
15791     /**
15792      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15793      */
15794     hideOnClick : false,
15795
15796     // private
15797     onRender : function(li){
15798         var s = document.createElement("span");
15799         s.className = this.itemCls;
15800         s.innerHTML = "&#160;";
15801         this.el = s;
15802         li.addClass("x-menu-sep-li");
15803         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
15804     }
15805 });/*
15806  * Based on:
15807  * Ext JS Library 1.1.1
15808  * Copyright(c) 2006-2007, Ext JS, LLC.
15809  *
15810  * Originally Released Under LGPL - original licence link has changed is not relivant.
15811  *
15812  * Fork - LGPL
15813  * <script type="text/javascript">
15814  */
15815 /**
15816  * @class Roo.menu.Item
15817  * @extends Roo.menu.BaseItem
15818  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
15819  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
15820  * activation and click handling.
15821  * @constructor
15822  * Creates a new Item
15823  * @param {Object} config Configuration options
15824  */
15825 Roo.menu.Item = function(config){
15826     Roo.menu.Item.superclass.constructor.call(this, config);
15827     if(this.menu){
15828         this.menu = Roo.menu.MenuMgr.get(this.menu);
15829     }
15830 };
15831 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
15832     
15833     /**
15834      * @cfg {String} text
15835      * The text to show on the menu item.
15836      */
15837     text: '',
15838      /**
15839      * @cfg {String} HTML to render in menu
15840      * The text to show on the menu item (HTML version).
15841      */
15842     html: '',
15843     /**
15844      * @cfg {String} icon
15845      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
15846      */
15847     icon: undefined,
15848     /**
15849      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
15850      */
15851     itemCls : "x-menu-item",
15852     /**
15853      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
15854      */
15855     canActivate : true,
15856     /**
15857      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
15858      */
15859     showDelay: 200,
15860     // doc'd in BaseItem
15861     hideDelay: 200,
15862
15863     // private
15864     ctype: "Roo.menu.Item",
15865     
15866     // private
15867     onRender : function(container, position){
15868         var el = document.createElement("a");
15869         el.hideFocus = true;
15870         el.unselectable = "on";
15871         el.href = this.href || "#";
15872         if(this.hrefTarget){
15873             el.target = this.hrefTarget;
15874         }
15875         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
15876         
15877         var html = this.html.length ? this.html  : String.format('{0}',this.text);
15878         
15879         el.innerHTML = String.format(
15880                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
15881                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
15882         this.el = el;
15883         Roo.menu.Item.superclass.onRender.call(this, container, position);
15884     },
15885
15886     /**
15887      * Sets the text to display in this menu item
15888      * @param {String} text The text to display
15889      * @param {Boolean} isHTML true to indicate text is pure html.
15890      */
15891     setText : function(text, isHTML){
15892         if (isHTML) {
15893             this.html = text;
15894         } else {
15895             this.text = text;
15896             this.html = '';
15897         }
15898         if(this.rendered){
15899             var html = this.html.length ? this.html  : String.format('{0}',this.text);
15900      
15901             this.el.update(String.format(
15902                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
15903                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
15904             this.parentMenu.autoWidth();
15905         }
15906     },
15907
15908     // private
15909     handleClick : function(e){
15910         if(!this.href){ // if no link defined, stop the event automatically
15911             e.stopEvent();
15912         }
15913         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
15914     },
15915
15916     // private
15917     activate : function(autoExpand){
15918         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
15919             this.focus();
15920             if(autoExpand){
15921                 this.expandMenu();
15922             }
15923         }
15924         return true;
15925     },
15926
15927     // private
15928     shouldDeactivate : function(e){
15929         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
15930             if(this.menu && this.menu.isVisible()){
15931                 return !this.menu.getEl().getRegion().contains(e.getPoint());
15932             }
15933             return true;
15934         }
15935         return false;
15936     },
15937
15938     // private
15939     deactivate : function(){
15940         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
15941         this.hideMenu();
15942     },
15943
15944     // private
15945     expandMenu : function(autoActivate){
15946         if(!this.disabled && this.menu){
15947             clearTimeout(this.hideTimer);
15948             delete this.hideTimer;
15949             if(!this.menu.isVisible() && !this.showTimer){
15950                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
15951             }else if (this.menu.isVisible() && autoActivate){
15952                 this.menu.tryActivate(0, 1);
15953             }
15954         }
15955     },
15956
15957     // private
15958     deferExpand : function(autoActivate){
15959         delete this.showTimer;
15960         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
15961         if(autoActivate){
15962             this.menu.tryActivate(0, 1);
15963         }
15964     },
15965
15966     // private
15967     hideMenu : function(){
15968         clearTimeout(this.showTimer);
15969         delete this.showTimer;
15970         if(!this.hideTimer && this.menu && this.menu.isVisible()){
15971             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
15972         }
15973     },
15974
15975     // private
15976     deferHide : function(){
15977         delete this.hideTimer;
15978         this.menu.hide();
15979     }
15980 });/*
15981  * Based on:
15982  * Ext JS Library 1.1.1
15983  * Copyright(c) 2006-2007, Ext JS, LLC.
15984  *
15985  * Originally Released Under LGPL - original licence link has changed is not relivant.
15986  *
15987  * Fork - LGPL
15988  * <script type="text/javascript">
15989  */
15990  
15991 /**
15992  * @class Roo.menu.CheckItem
15993  * @extends Roo.menu.Item
15994  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
15995  * @constructor
15996  * Creates a new CheckItem
15997  * @param {Object} config Configuration options
15998  */
15999 Roo.menu.CheckItem = function(config){
16000     Roo.menu.CheckItem.superclass.constructor.call(this, config);
16001     this.addEvents({
16002         /**
16003          * @event beforecheckchange
16004          * Fires before the checked value is set, providing an opportunity to cancel if needed
16005          * @param {Roo.menu.CheckItem} this
16006          * @param {Boolean} checked The new checked value that will be set
16007          */
16008         "beforecheckchange" : true,
16009         /**
16010          * @event checkchange
16011          * Fires after the checked value has been set
16012          * @param {Roo.menu.CheckItem} this
16013          * @param {Boolean} checked The checked value that was set
16014          */
16015         "checkchange" : true
16016     });
16017     if(this.checkHandler){
16018         this.on('checkchange', this.checkHandler, this.scope);
16019     }
16020 };
16021 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
16022     /**
16023      * @cfg {String} group
16024      * All check items with the same group name will automatically be grouped into a single-select
16025      * radio button group (defaults to '')
16026      */
16027     /**
16028      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
16029      */
16030     itemCls : "x-menu-item x-menu-check-item",
16031     /**
16032      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
16033      */
16034     groupClass : "x-menu-group-item",
16035
16036     /**
16037      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
16038      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
16039      * initialized with checked = true will be rendered as checked.
16040      */
16041     checked: false,
16042
16043     // private
16044     ctype: "Roo.menu.CheckItem",
16045
16046     // private
16047     onRender : function(c){
16048         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
16049         if(this.group){
16050             this.el.addClass(this.groupClass);
16051         }
16052         Roo.menu.MenuMgr.registerCheckable(this);
16053         if(this.checked){
16054             this.checked = false;
16055             this.setChecked(true, true);
16056         }
16057     },
16058
16059     // private
16060     destroy : function(){
16061         if(this.rendered){
16062             Roo.menu.MenuMgr.unregisterCheckable(this);
16063         }
16064         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
16065     },
16066
16067     /**
16068      * Set the checked state of this item
16069      * @param {Boolean} checked The new checked value
16070      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
16071      */
16072     setChecked : function(state, suppressEvent){
16073         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
16074             if(this.container){
16075                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
16076             }
16077             this.checked = state;
16078             if(suppressEvent !== true){
16079                 this.fireEvent("checkchange", this, state);
16080             }
16081         }
16082     },
16083
16084     // private
16085     handleClick : function(e){
16086        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
16087            this.setChecked(!this.checked);
16088        }
16089        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
16090     }
16091 });/*
16092  * Based on:
16093  * Ext JS Library 1.1.1
16094  * Copyright(c) 2006-2007, Ext JS, LLC.
16095  *
16096  * Originally Released Under LGPL - original licence link has changed is not relivant.
16097  *
16098  * Fork - LGPL
16099  * <script type="text/javascript">
16100  */
16101  
16102 /**
16103  * @class Roo.menu.DateItem
16104  * @extends Roo.menu.Adapter
16105  * A menu item that wraps the {@link Roo.DatPicker} component.
16106  * @constructor
16107  * Creates a new DateItem
16108  * @param {Object} config Configuration options
16109  */
16110 Roo.menu.DateItem = function(config){
16111     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
16112     /** The Roo.DatePicker object @type Roo.DatePicker */
16113     this.picker = this.component;
16114     this.addEvents({select: true});
16115     
16116     this.picker.on("render", function(picker){
16117         picker.getEl().swallowEvent("click");
16118         picker.container.addClass("x-menu-date-item");
16119     });
16120
16121     this.picker.on("select", this.onSelect, this);
16122 };
16123
16124 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
16125     // private
16126     onSelect : function(picker, date){
16127         this.fireEvent("select", this, date, picker);
16128         Roo.menu.DateItem.superclass.handleClick.call(this);
16129     }
16130 });/*
16131  * Based on:
16132  * Ext JS Library 1.1.1
16133  * Copyright(c) 2006-2007, Ext JS, LLC.
16134  *
16135  * Originally Released Under LGPL - original licence link has changed is not relivant.
16136  *
16137  * Fork - LGPL
16138  * <script type="text/javascript">
16139  */
16140  
16141 /**
16142  * @class Roo.menu.ColorItem
16143  * @extends Roo.menu.Adapter
16144  * A menu item that wraps the {@link Roo.ColorPalette} component.
16145  * @constructor
16146  * Creates a new ColorItem
16147  * @param {Object} config Configuration options
16148  */
16149 Roo.menu.ColorItem = function(config){
16150     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
16151     /** The Roo.ColorPalette object @type Roo.ColorPalette */
16152     this.palette = this.component;
16153     this.relayEvents(this.palette, ["select"]);
16154     if(this.selectHandler){
16155         this.on('select', this.selectHandler, this.scope);
16156     }
16157 };
16158 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
16159  * Based on:
16160  * Ext JS Library 1.1.1
16161  * Copyright(c) 2006-2007, Ext JS, LLC.
16162  *
16163  * Originally Released Under LGPL - original licence link has changed is not relivant.
16164  *
16165  * Fork - LGPL
16166  * <script type="text/javascript">
16167  */
16168  
16169
16170 /**
16171  * @class Roo.menu.DateMenu
16172  * @extends Roo.menu.Menu
16173  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
16174  * @constructor
16175  * Creates a new DateMenu
16176  * @param {Object} config Configuration options
16177  */
16178 Roo.menu.DateMenu = function(config){
16179     Roo.menu.DateMenu.superclass.constructor.call(this, config);
16180     this.plain = true;
16181     var di = new Roo.menu.DateItem(config);
16182     this.add(di);
16183     /**
16184      * The {@link Roo.DatePicker} instance for this DateMenu
16185      * @type DatePicker
16186      */
16187     this.picker = di.picker;
16188     /**
16189      * @event select
16190      * @param {DatePicker} picker
16191      * @param {Date} date
16192      */
16193     this.relayEvents(di, ["select"]);
16194     this.on('beforeshow', function(){
16195         if(this.picker){
16196             this.picker.hideMonthPicker(false);
16197         }
16198     }, this);
16199 };
16200 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
16201     cls:'x-date-menu'
16202 });/*
16203  * Based on:
16204  * Ext JS Library 1.1.1
16205  * Copyright(c) 2006-2007, Ext JS, LLC.
16206  *
16207  * Originally Released Under LGPL - original licence link has changed is not relivant.
16208  *
16209  * Fork - LGPL
16210  * <script type="text/javascript">
16211  */
16212  
16213
16214 /**
16215  * @class Roo.menu.ColorMenu
16216  * @extends Roo.menu.Menu
16217  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
16218  * @constructor
16219  * Creates a new ColorMenu
16220  * @param {Object} config Configuration options
16221  */
16222 Roo.menu.ColorMenu = function(config){
16223     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
16224     this.plain = true;
16225     var ci = new Roo.menu.ColorItem(config);
16226     this.add(ci);
16227     /**
16228      * The {@link Roo.ColorPalette} instance for this ColorMenu
16229      * @type ColorPalette
16230      */
16231     this.palette = ci.palette;
16232     /**
16233      * @event select
16234      * @param {ColorPalette} palette
16235      * @param {String} color
16236      */
16237     this.relayEvents(ci, ["select"]);
16238 };
16239 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
16240  * Based on:
16241  * Ext JS Library 1.1.1
16242  * Copyright(c) 2006-2007, Ext JS, LLC.
16243  *
16244  * Originally Released Under LGPL - original licence link has changed is not relivant.
16245  *
16246  * Fork - LGPL
16247  * <script type="text/javascript">
16248  */
16249  
16250 /**
16251  * @class Roo.form.Field
16252  * @extends Roo.BoxComponent
16253  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
16254  * @constructor
16255  * Creates a new Field
16256  * @param {Object} config Configuration options
16257  */
16258 Roo.form.Field = function(config){
16259     Roo.form.Field.superclass.constructor.call(this, config);
16260 };
16261
16262 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
16263     /**
16264      * @cfg {String} fieldLabel Label to use when rendering a form.
16265      */
16266        /**
16267      * @cfg {String} qtip Mouse over tip
16268      */
16269      
16270     /**
16271      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
16272      */
16273     invalidClass : "x-form-invalid",
16274     /**
16275      * @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")
16276      */
16277     invalidText : "The value in this field is invalid",
16278     /**
16279      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
16280      */
16281     focusClass : "x-form-focus",
16282     /**
16283      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
16284       automatic validation (defaults to "keyup").
16285      */
16286     validationEvent : "keyup",
16287     /**
16288      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
16289      */
16290     validateOnBlur : true,
16291     /**
16292      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
16293      */
16294     validationDelay : 250,
16295     /**
16296      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
16297      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
16298      */
16299     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
16300     /**
16301      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
16302      */
16303     fieldClass : "x-form-field",
16304     /**
16305      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
16306      *<pre>
16307 Value         Description
16308 -----------   ----------------------------------------------------------------------
16309 qtip          Display a quick tip when the user hovers over the field
16310 title         Display a default browser title attribute popup
16311 under         Add a block div beneath the field containing the error text
16312 side          Add an error icon to the right of the field with a popup on hover
16313 [element id]  Add the error text directly to the innerHTML of the specified element
16314 </pre>
16315      */
16316     msgTarget : 'qtip',
16317     /**
16318      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
16319      */
16320     msgFx : 'normal',
16321
16322     /**
16323      * @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.
16324      */
16325     readOnly : false,
16326
16327     /**
16328      * @cfg {Boolean} disabled True to disable the field (defaults to false).
16329      */
16330     disabled : false,
16331
16332     /**
16333      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
16334      */
16335     inputType : undefined,
16336     
16337     /**
16338      * @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).
16339          */
16340         tabIndex : undefined,
16341         
16342     // private
16343     isFormField : true,
16344
16345     // private
16346     hasFocus : false,
16347     /**
16348      * @property {Roo.Element} fieldEl
16349      * Element Containing the rendered Field (with label etc.)
16350      */
16351     /**
16352      * @cfg {Mixed} value A value to initialize this field with.
16353      */
16354     value : undefined,
16355
16356     /**
16357      * @cfg {String} name The field's HTML name attribute.
16358      */
16359     /**
16360      * @cfg {String} cls A CSS class to apply to the field's underlying element.
16361      */
16362     // private
16363     loadedValue : false,
16364      
16365      
16366         // private ??
16367         initComponent : function(){
16368         Roo.form.Field.superclass.initComponent.call(this);
16369         this.addEvents({
16370             /**
16371              * @event focus
16372              * Fires when this field receives input focus.
16373              * @param {Roo.form.Field} this
16374              */
16375             focus : true,
16376             /**
16377              * @event blur
16378              * Fires when this field loses input focus.
16379              * @param {Roo.form.Field} this
16380              */
16381             blur : true,
16382             /**
16383              * @event specialkey
16384              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
16385              * {@link Roo.EventObject#getKey} to determine which key was pressed.
16386              * @param {Roo.form.Field} this
16387              * @param {Roo.EventObject} e The event object
16388              */
16389             specialkey : true,
16390             /**
16391              * @event change
16392              * Fires just before the field blurs if the field value has changed.
16393              * @param {Roo.form.Field} this
16394              * @param {Mixed} newValue The new value
16395              * @param {Mixed} oldValue The original value
16396              */
16397             change : true,
16398             /**
16399              * @event invalid
16400              * Fires after the field has been marked as invalid.
16401              * @param {Roo.form.Field} this
16402              * @param {String} msg The validation message
16403              */
16404             invalid : true,
16405             /**
16406              * @event valid
16407              * Fires after the field has been validated with no errors.
16408              * @param {Roo.form.Field} this
16409              */
16410             valid : true,
16411              /**
16412              * @event keyup
16413              * Fires after the key up
16414              * @param {Roo.form.Field} this
16415              * @param {Roo.EventObject}  e The event Object
16416              */
16417             keyup : true
16418         });
16419     },
16420
16421     /**
16422      * Returns the name attribute of the field if available
16423      * @return {String} name The field name
16424      */
16425     getName: function(){
16426          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
16427     },
16428
16429     // private
16430     onRender : function(ct, position){
16431         Roo.form.Field.superclass.onRender.call(this, ct, position);
16432         if(!this.el){
16433             var cfg = this.getAutoCreate();
16434             if(!cfg.name){
16435                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
16436             }
16437             if (!cfg.name.length) {
16438                 delete cfg.name;
16439             }
16440             if(this.inputType){
16441                 cfg.type = this.inputType;
16442             }
16443             this.el = ct.createChild(cfg, position);
16444         }
16445         var type = this.el.dom.type;
16446         if(type){
16447             if(type == 'password'){
16448                 type = 'text';
16449             }
16450             this.el.addClass('x-form-'+type);
16451         }
16452         if(this.readOnly){
16453             this.el.dom.readOnly = true;
16454         }
16455         if(this.tabIndex !== undefined){
16456             this.el.dom.setAttribute('tabIndex', this.tabIndex);
16457         }
16458
16459         this.el.addClass([this.fieldClass, this.cls]);
16460         this.initValue();
16461     },
16462
16463     /**
16464      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
16465      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
16466      * @return {Roo.form.Field} this
16467      */
16468     applyTo : function(target){
16469         this.allowDomMove = false;
16470         this.el = Roo.get(target);
16471         this.render(this.el.dom.parentNode);
16472         return this;
16473     },
16474
16475     // private
16476     initValue : function(){
16477         if(this.value !== undefined){
16478             this.setValue(this.value);
16479         }else if(this.el.dom.value.length > 0){
16480             this.setValue(this.el.dom.value);
16481         }
16482     },
16483
16484     /**
16485      * Returns true if this field has been changed since it was originally loaded and is not disabled.
16486      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
16487      */
16488     isDirty : function() {
16489         if(this.disabled) {
16490             return false;
16491         }
16492         return String(this.getValue()) !== String(this.originalValue);
16493     },
16494
16495     /**
16496      * stores the current value in loadedValue
16497      */
16498     resetHasChanged : function()
16499     {
16500         this.loadedValue = String(this.getValue());
16501     },
16502     /**
16503      * checks the current value against the 'loaded' value.
16504      * Note - will return false if 'resetHasChanged' has not been called first.
16505      */
16506     hasChanged : function()
16507     {
16508         if(this.disabled || this.readOnly) {
16509             return false;
16510         }
16511         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
16512     },
16513     
16514     
16515     
16516     // private
16517     afterRender : function(){
16518         Roo.form.Field.superclass.afterRender.call(this);
16519         this.initEvents();
16520     },
16521
16522     // private
16523     fireKey : function(e){
16524         //Roo.log('field ' + e.getKey());
16525         if(e.isNavKeyPress()){
16526             this.fireEvent("specialkey", this, e);
16527         }
16528     },
16529
16530     /**
16531      * Resets the current field value to the originally loaded value and clears any validation messages
16532      */
16533     reset : function(){
16534         this.setValue(this.resetValue);
16535         this.clearInvalid();
16536     },
16537
16538     // private
16539     initEvents : function(){
16540         // safari killled keypress - so keydown is now used..
16541         this.el.on("keydown" , this.fireKey,  this);
16542         this.el.on("focus", this.onFocus,  this);
16543         this.el.on("blur", this.onBlur,  this);
16544         this.el.relayEvent('keyup', this);
16545
16546         // reference to original value for reset
16547         this.originalValue = this.getValue();
16548         this.resetValue =  this.getValue();
16549     },
16550
16551     // private
16552     onFocus : function(){
16553         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16554             this.el.addClass(this.focusClass);
16555         }
16556         if(!this.hasFocus){
16557             this.hasFocus = true;
16558             this.startValue = this.getValue();
16559             this.fireEvent("focus", this);
16560         }
16561     },
16562
16563     beforeBlur : Roo.emptyFn,
16564
16565     // private
16566     onBlur : function(){
16567         this.beforeBlur();
16568         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16569             this.el.removeClass(this.focusClass);
16570         }
16571         this.hasFocus = false;
16572         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
16573             this.validate();
16574         }
16575         var v = this.getValue();
16576         if(String(v) !== String(this.startValue)){
16577             this.fireEvent('change', this, v, this.startValue);
16578         }
16579         this.fireEvent("blur", this);
16580     },
16581
16582     /**
16583      * Returns whether or not the field value is currently valid
16584      * @param {Boolean} preventMark True to disable marking the field invalid
16585      * @return {Boolean} True if the value is valid, else false
16586      */
16587     isValid : function(preventMark){
16588         if(this.disabled){
16589             return true;
16590         }
16591         var restore = this.preventMark;
16592         this.preventMark = preventMark === true;
16593         var v = this.validateValue(this.processValue(this.getRawValue()));
16594         this.preventMark = restore;
16595         return v;
16596     },
16597
16598     /**
16599      * Validates the field value
16600      * @return {Boolean} True if the value is valid, else false
16601      */
16602     validate : function(){
16603         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
16604             this.clearInvalid();
16605             return true;
16606         }
16607         return false;
16608     },
16609
16610     processValue : function(value){
16611         return value;
16612     },
16613
16614     // private
16615     // Subclasses should provide the validation implementation by overriding this
16616     validateValue : function(value){
16617         return true;
16618     },
16619
16620     /**
16621      * Mark this field as invalid
16622      * @param {String} msg The validation message
16623      */
16624     markInvalid : function(msg){
16625         if(!this.rendered || this.preventMark){ // not rendered
16626             return;
16627         }
16628         
16629         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16630         
16631         obj.el.addClass(this.invalidClass);
16632         msg = msg || this.invalidText;
16633         switch(this.msgTarget){
16634             case 'qtip':
16635                 obj.el.dom.qtip = msg;
16636                 obj.el.dom.qclass = 'x-form-invalid-tip';
16637                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
16638                     Roo.QuickTips.enable();
16639                 }
16640                 break;
16641             case 'title':
16642                 this.el.dom.title = msg;
16643                 break;
16644             case 'under':
16645                 if(!this.errorEl){
16646                     var elp = this.el.findParent('.x-form-element', 5, true);
16647                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
16648                     this.errorEl.setWidth(elp.getWidth(true)-20);
16649                 }
16650                 this.errorEl.update(msg);
16651                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
16652                 break;
16653             case 'side':
16654                 if(!this.errorIcon){
16655                     var elp = this.el.findParent('.x-form-element', 5, true);
16656                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
16657                 }
16658                 this.alignErrorIcon();
16659                 this.errorIcon.dom.qtip = msg;
16660                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
16661                 this.errorIcon.show();
16662                 this.on('resize', this.alignErrorIcon, this);
16663                 break;
16664             default:
16665                 var t = Roo.getDom(this.msgTarget);
16666                 t.innerHTML = msg;
16667                 t.style.display = this.msgDisplay;
16668                 break;
16669         }
16670         this.fireEvent('invalid', this, msg);
16671     },
16672
16673     // private
16674     alignErrorIcon : function(){
16675         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
16676     },
16677
16678     /**
16679      * Clear any invalid styles/messages for this field
16680      */
16681     clearInvalid : function(){
16682         if(!this.rendered || this.preventMark){ // not rendered
16683             return;
16684         }
16685         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16686         
16687         obj.el.removeClass(this.invalidClass);
16688         switch(this.msgTarget){
16689             case 'qtip':
16690                 obj.el.dom.qtip = '';
16691                 break;
16692             case 'title':
16693                 this.el.dom.title = '';
16694                 break;
16695             case 'under':
16696                 if(this.errorEl){
16697                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
16698                 }
16699                 break;
16700             case 'side':
16701                 if(this.errorIcon){
16702                     this.errorIcon.dom.qtip = '';
16703                     this.errorIcon.hide();
16704                     this.un('resize', this.alignErrorIcon, this);
16705                 }
16706                 break;
16707             default:
16708                 var t = Roo.getDom(this.msgTarget);
16709                 t.innerHTML = '';
16710                 t.style.display = 'none';
16711                 break;
16712         }
16713         this.fireEvent('valid', this);
16714     },
16715
16716     /**
16717      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
16718      * @return {Mixed} value The field value
16719      */
16720     getRawValue : function(){
16721         var v = this.el.getValue();
16722         
16723         return v;
16724     },
16725
16726     /**
16727      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16728      * @return {Mixed} value The field value
16729      */
16730     getValue : function(){
16731         var v = this.el.getValue();
16732          
16733         return v;
16734     },
16735
16736     /**
16737      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
16738      * @param {Mixed} value The value to set
16739      */
16740     setRawValue : function(v){
16741         return this.el.dom.value = (v === null || v === undefined ? '' : v);
16742     },
16743
16744     /**
16745      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
16746      * @param {Mixed} value The value to set
16747      */
16748     setValue : function(v){
16749         this.value = v;
16750         if(this.rendered){
16751             this.el.dom.value = (v === null || v === undefined ? '' : v);
16752              this.validate();
16753         }
16754     },
16755
16756     adjustSize : function(w, h){
16757         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
16758         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
16759         return s;
16760     },
16761
16762     adjustWidth : function(tag, w){
16763         tag = tag.toLowerCase();
16764         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
16765             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
16766                 if(tag == 'input'){
16767                     return w + 2;
16768                 }
16769                 if(tag == 'textarea'){
16770                     return w-2;
16771                 }
16772             }else if(Roo.isOpera){
16773                 if(tag == 'input'){
16774                     return w + 2;
16775                 }
16776                 if(tag == 'textarea'){
16777                     return w-2;
16778                 }
16779             }
16780         }
16781         return w;
16782     }
16783 });
16784
16785
16786 // anything other than normal should be considered experimental
16787 Roo.form.Field.msgFx = {
16788     normal : {
16789         show: function(msgEl, f){
16790             msgEl.setDisplayed('block');
16791         },
16792
16793         hide : function(msgEl, f){
16794             msgEl.setDisplayed(false).update('');
16795         }
16796     },
16797
16798     slide : {
16799         show: function(msgEl, f){
16800             msgEl.slideIn('t', {stopFx:true});
16801         },
16802
16803         hide : function(msgEl, f){
16804             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
16805         }
16806     },
16807
16808     slideRight : {
16809         show: function(msgEl, f){
16810             msgEl.fixDisplay();
16811             msgEl.alignTo(f.el, 'tl-tr');
16812             msgEl.slideIn('l', {stopFx:true});
16813         },
16814
16815         hide : function(msgEl, f){
16816             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
16817         }
16818     }
16819 };/*
16820  * Based on:
16821  * Ext JS Library 1.1.1
16822  * Copyright(c) 2006-2007, Ext JS, LLC.
16823  *
16824  * Originally Released Under LGPL - original licence link has changed is not relivant.
16825  *
16826  * Fork - LGPL
16827  * <script type="text/javascript">
16828  */
16829  
16830
16831 /**
16832  * @class Roo.form.TextField
16833  * @extends Roo.form.Field
16834  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
16835  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
16836  * @constructor
16837  * Creates a new TextField
16838  * @param {Object} config Configuration options
16839  */
16840 Roo.form.TextField = function(config){
16841     Roo.form.TextField.superclass.constructor.call(this, config);
16842     this.addEvents({
16843         /**
16844          * @event autosize
16845          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
16846          * according to the default logic, but this event provides a hook for the developer to apply additional
16847          * logic at runtime to resize the field if needed.
16848              * @param {Roo.form.Field} this This text field
16849              * @param {Number} width The new field width
16850              */
16851         autosize : true
16852     });
16853 };
16854
16855 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
16856     /**
16857      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
16858      */
16859     grow : false,
16860     /**
16861      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
16862      */
16863     growMin : 30,
16864     /**
16865      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
16866      */
16867     growMax : 800,
16868     /**
16869      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
16870      */
16871     vtype : null,
16872     /**
16873      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
16874      */
16875     maskRe : null,
16876     /**
16877      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
16878      */
16879     disableKeyFilter : false,
16880     /**
16881      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
16882      */
16883     allowBlank : true,
16884     /**
16885      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
16886      */
16887     minLength : 0,
16888     /**
16889      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
16890      */
16891     maxLength : Number.MAX_VALUE,
16892     /**
16893      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
16894      */
16895     minLengthText : "The minimum length for this field is {0}",
16896     /**
16897      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
16898      */
16899     maxLengthText : "The maximum length for this field is {0}",
16900     /**
16901      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
16902      */
16903     selectOnFocus : false,
16904     /**
16905      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
16906      */
16907     blankText : "This field is required",
16908     /**
16909      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
16910      * If available, this function will be called only after the basic validators all return true, and will be passed the
16911      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
16912      */
16913     validator : null,
16914     /**
16915      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
16916      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
16917      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
16918      */
16919     regex : null,
16920     /**
16921      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
16922      */
16923     regexText : "",
16924     /**
16925      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
16926      */
16927     emptyText : null,
16928    
16929
16930     // private
16931     initEvents : function()
16932     {
16933         if (this.emptyText) {
16934             this.el.attr('placeholder', this.emptyText);
16935         }
16936         
16937         Roo.form.TextField.superclass.initEvents.call(this);
16938         if(this.validationEvent == 'keyup'){
16939             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
16940             this.el.on('keyup', this.filterValidation, this);
16941         }
16942         else if(this.validationEvent !== false){
16943             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
16944         }
16945         
16946         if(this.selectOnFocus){
16947             this.on("focus", this.preFocus, this);
16948             
16949         }
16950         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
16951             this.el.on("keypress", this.filterKeys, this);
16952         }
16953         if(this.grow){
16954             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
16955             this.el.on("click", this.autoSize,  this);
16956         }
16957         if(this.el.is('input[type=password]') && Roo.isSafari){
16958             this.el.on('keydown', this.SafariOnKeyDown, this);
16959         }
16960     },
16961
16962     processValue : function(value){
16963         if(this.stripCharsRe){
16964             var newValue = value.replace(this.stripCharsRe, '');
16965             if(newValue !== value){
16966                 this.setRawValue(newValue);
16967                 return newValue;
16968             }
16969         }
16970         return value;
16971     },
16972
16973     filterValidation : function(e){
16974         if(!e.isNavKeyPress()){
16975             this.validationTask.delay(this.validationDelay);
16976         }
16977     },
16978
16979     // private
16980     onKeyUp : function(e){
16981         if(!e.isNavKeyPress()){
16982             this.autoSize();
16983         }
16984     },
16985
16986     /**
16987      * Resets the current field value to the originally-loaded value and clears any validation messages.
16988      *  
16989      */
16990     reset : function(){
16991         Roo.form.TextField.superclass.reset.call(this);
16992        
16993     },
16994
16995     
16996     // private
16997     preFocus : function(){
16998         
16999         if(this.selectOnFocus){
17000             this.el.dom.select();
17001         }
17002     },
17003
17004     
17005     // private
17006     filterKeys : function(e){
17007         var k = e.getKey();
17008         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
17009             return;
17010         }
17011         var c = e.getCharCode(), cc = String.fromCharCode(c);
17012         if(Roo.isIE && (e.isSpecialKey() || !cc)){
17013             return;
17014         }
17015         if(!this.maskRe.test(cc)){
17016             e.stopEvent();
17017         }
17018     },
17019
17020     setValue : function(v){
17021         
17022         Roo.form.TextField.superclass.setValue.apply(this, arguments);
17023         
17024         this.autoSize();
17025     },
17026
17027     /**
17028      * Validates a value according to the field's validation rules and marks the field as invalid
17029      * if the validation fails
17030      * @param {Mixed} value The value to validate
17031      * @return {Boolean} True if the value is valid, else false
17032      */
17033     validateValue : function(value){
17034         if(value.length < 1)  { // if it's blank
17035              if(this.allowBlank){
17036                 this.clearInvalid();
17037                 return true;
17038              }else{
17039                 this.markInvalid(this.blankText);
17040                 return false;
17041              }
17042         }
17043         if(value.length < this.minLength){
17044             this.markInvalid(String.format(this.minLengthText, this.minLength));
17045             return false;
17046         }
17047         if(value.length > this.maxLength){
17048             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
17049             return false;
17050         }
17051         if(this.vtype){
17052             var vt = Roo.form.VTypes;
17053             if(!vt[this.vtype](value, this)){
17054                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
17055                 return false;
17056             }
17057         }
17058         if(typeof this.validator == "function"){
17059             var msg = this.validator(value);
17060             if(msg !== true){
17061                 this.markInvalid(msg);
17062                 return false;
17063             }
17064         }
17065         if(this.regex && !this.regex.test(value)){
17066             this.markInvalid(this.regexText);
17067             return false;
17068         }
17069         return true;
17070     },
17071
17072     /**
17073      * Selects text in this field
17074      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
17075      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
17076      */
17077     selectText : function(start, end){
17078         var v = this.getRawValue();
17079         if(v.length > 0){
17080             start = start === undefined ? 0 : start;
17081             end = end === undefined ? v.length : end;
17082             var d = this.el.dom;
17083             if(d.setSelectionRange){
17084                 d.setSelectionRange(start, end);
17085             }else if(d.createTextRange){
17086                 var range = d.createTextRange();
17087                 range.moveStart("character", start);
17088                 range.moveEnd("character", v.length-end);
17089                 range.select();
17090             }
17091         }
17092     },
17093
17094     /**
17095      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
17096      * This only takes effect if grow = true, and fires the autosize event.
17097      */
17098     autoSize : function(){
17099         if(!this.grow || !this.rendered){
17100             return;
17101         }
17102         if(!this.metrics){
17103             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
17104         }
17105         var el = this.el;
17106         var v = el.dom.value;
17107         var d = document.createElement('div');
17108         d.appendChild(document.createTextNode(v));
17109         v = d.innerHTML;
17110         d = null;
17111         v += "&#160;";
17112         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
17113         this.el.setWidth(w);
17114         this.fireEvent("autosize", this, w);
17115     },
17116     
17117     // private
17118     SafariOnKeyDown : function(event)
17119     {
17120         // this is a workaround for a password hang bug on chrome/ webkit.
17121         
17122         var isSelectAll = false;
17123         
17124         if(this.el.dom.selectionEnd > 0){
17125             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
17126         }
17127         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
17128             event.preventDefault();
17129             this.setValue('');
17130             return;
17131         }
17132         
17133         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
17134             
17135             event.preventDefault();
17136             // this is very hacky as keydown always get's upper case.
17137             
17138             var cc = String.fromCharCode(event.getCharCode());
17139             
17140             
17141             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
17142             
17143         }
17144         
17145         
17146     }
17147 });/*
17148  * Based on:
17149  * Ext JS Library 1.1.1
17150  * Copyright(c) 2006-2007, Ext JS, LLC.
17151  *
17152  * Originally Released Under LGPL - original licence link has changed is not relivant.
17153  *
17154  * Fork - LGPL
17155  * <script type="text/javascript">
17156  */
17157  
17158 /**
17159  * @class Roo.form.Hidden
17160  * @extends Roo.form.TextField
17161  * Simple Hidden element used on forms 
17162  * 
17163  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
17164  * 
17165  * @constructor
17166  * Creates a new Hidden form element.
17167  * @param {Object} config Configuration options
17168  */
17169
17170
17171
17172 // easy hidden field...
17173 Roo.form.Hidden = function(config){
17174     Roo.form.Hidden.superclass.constructor.call(this, config);
17175 };
17176   
17177 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
17178     fieldLabel:      '',
17179     inputType:      'hidden',
17180     width:          50,
17181     allowBlank:     true,
17182     labelSeparator: '',
17183     hidden:         true,
17184     itemCls :       'x-form-item-display-none'
17185
17186
17187 });
17188
17189
17190 /*
17191  * Based on:
17192  * Ext JS Library 1.1.1
17193  * Copyright(c) 2006-2007, Ext JS, LLC.
17194  *
17195  * Originally Released Under LGPL - original licence link has changed is not relivant.
17196  *
17197  * Fork - LGPL
17198  * <script type="text/javascript">
17199  */
17200  
17201 /**
17202  * @class Roo.form.TriggerField
17203  * @extends Roo.form.TextField
17204  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
17205  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
17206  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
17207  * for which you can provide a custom implementation.  For example:
17208  * <pre><code>
17209 var trigger = new Roo.form.TriggerField();
17210 trigger.onTriggerClick = myTriggerFn;
17211 trigger.applyTo('my-field');
17212 </code></pre>
17213  *
17214  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
17215  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
17216  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
17217  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
17218  * @constructor
17219  * Create a new TriggerField.
17220  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
17221  * to the base TextField)
17222  */
17223 Roo.form.TriggerField = function(config){
17224     this.mimicing = false;
17225     Roo.form.TriggerField.superclass.constructor.call(this, config);
17226 };
17227
17228 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
17229     /**
17230      * @cfg {String} triggerClass A CSS class to apply to the trigger
17231      */
17232     /**
17233      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17234      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
17235      */
17236     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
17237     /**
17238      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
17239      */
17240     hideTrigger:false,
17241
17242     /** @cfg {Boolean} grow @hide */
17243     /** @cfg {Number} growMin @hide */
17244     /** @cfg {Number} growMax @hide */
17245
17246     /**
17247      * @hide 
17248      * @method
17249      */
17250     autoSize: Roo.emptyFn,
17251     // private
17252     monitorTab : true,
17253     // private
17254     deferHeight : true,
17255
17256     
17257     actionMode : 'wrap',
17258     // private
17259     onResize : function(w, h){
17260         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
17261         if(typeof w == 'number'){
17262             var x = w - this.trigger.getWidth();
17263             this.el.setWidth(this.adjustWidth('input', x));
17264             this.trigger.setStyle('left', x+'px');
17265         }
17266     },
17267
17268     // private
17269     adjustSize : Roo.BoxComponent.prototype.adjustSize,
17270
17271     // private
17272     getResizeEl : function(){
17273         return this.wrap;
17274     },
17275
17276     // private
17277     getPositionEl : function(){
17278         return this.wrap;
17279     },
17280
17281     // private
17282     alignErrorIcon : function(){
17283         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
17284     },
17285
17286     // private
17287     onRender : function(ct, position){
17288         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
17289         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
17290         this.trigger = this.wrap.createChild(this.triggerConfig ||
17291                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
17292         if(this.hideTrigger){
17293             this.trigger.setDisplayed(false);
17294         }
17295         this.initTrigger();
17296         if(!this.width){
17297             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
17298         }
17299     },
17300
17301     // private
17302     initTrigger : function(){
17303         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
17304         this.trigger.addClassOnOver('x-form-trigger-over');
17305         this.trigger.addClassOnClick('x-form-trigger-click');
17306     },
17307
17308     // private
17309     onDestroy : function(){
17310         if(this.trigger){
17311             this.trigger.removeAllListeners();
17312             this.trigger.remove();
17313         }
17314         if(this.wrap){
17315             this.wrap.remove();
17316         }
17317         Roo.form.TriggerField.superclass.onDestroy.call(this);
17318     },
17319
17320     // private
17321     onFocus : function(){
17322         Roo.form.TriggerField.superclass.onFocus.call(this);
17323         if(!this.mimicing){
17324             this.wrap.addClass('x-trigger-wrap-focus');
17325             this.mimicing = true;
17326             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
17327             if(this.monitorTab){
17328                 this.el.on("keydown", this.checkTab, this);
17329             }
17330         }
17331     },
17332
17333     // private
17334     checkTab : function(e){
17335         if(e.getKey() == e.TAB){
17336             this.triggerBlur();
17337         }
17338     },
17339
17340     // private
17341     onBlur : function(){
17342         // do nothing
17343     },
17344
17345     // private
17346     mimicBlur : function(e, t){
17347         if(!this.wrap.contains(t) && this.validateBlur()){
17348             this.triggerBlur();
17349         }
17350     },
17351
17352     // private
17353     triggerBlur : function(){
17354         this.mimicing = false;
17355         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
17356         if(this.monitorTab){
17357             this.el.un("keydown", this.checkTab, this);
17358         }
17359         this.wrap.removeClass('x-trigger-wrap-focus');
17360         Roo.form.TriggerField.superclass.onBlur.call(this);
17361     },
17362
17363     // private
17364     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
17365     validateBlur : function(e, t){
17366         return true;
17367     },
17368
17369     // private
17370     onDisable : function(){
17371         Roo.form.TriggerField.superclass.onDisable.call(this);
17372         if(this.wrap){
17373             this.wrap.addClass('x-item-disabled');
17374         }
17375     },
17376
17377     // private
17378     onEnable : function(){
17379         Roo.form.TriggerField.superclass.onEnable.call(this);
17380         if(this.wrap){
17381             this.wrap.removeClass('x-item-disabled');
17382         }
17383     },
17384
17385     // private
17386     onShow : function(){
17387         var ae = this.getActionEl();
17388         
17389         if(ae){
17390             ae.dom.style.display = '';
17391             ae.dom.style.visibility = 'visible';
17392         }
17393     },
17394
17395     // private
17396     
17397     onHide : function(){
17398         var ae = this.getActionEl();
17399         ae.dom.style.display = 'none';
17400     },
17401
17402     /**
17403      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
17404      * by an implementing function.
17405      * @method
17406      * @param {EventObject} e
17407      */
17408     onTriggerClick : Roo.emptyFn
17409 });
17410
17411 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
17412 // to be extended by an implementing class.  For an example of implementing this class, see the custom
17413 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
17414 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
17415     initComponent : function(){
17416         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
17417
17418         this.triggerConfig = {
17419             tag:'span', cls:'x-form-twin-triggers', cn:[
17420             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
17421             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
17422         ]};
17423     },
17424
17425     getTrigger : function(index){
17426         return this.triggers[index];
17427     },
17428
17429     initTrigger : function(){
17430         var ts = this.trigger.select('.x-form-trigger', true);
17431         this.wrap.setStyle('overflow', 'hidden');
17432         var triggerField = this;
17433         ts.each(function(t, all, index){
17434             t.hide = function(){
17435                 var w = triggerField.wrap.getWidth();
17436                 this.dom.style.display = 'none';
17437                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17438             };
17439             t.show = function(){
17440                 var w = triggerField.wrap.getWidth();
17441                 this.dom.style.display = '';
17442                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17443             };
17444             var triggerIndex = 'Trigger'+(index+1);
17445
17446             if(this['hide'+triggerIndex]){
17447                 t.dom.style.display = 'none';
17448             }
17449             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
17450             t.addClassOnOver('x-form-trigger-over');
17451             t.addClassOnClick('x-form-trigger-click');
17452         }, this);
17453         this.triggers = ts.elements;
17454     },
17455
17456     onTrigger1Click : Roo.emptyFn,
17457     onTrigger2Click : Roo.emptyFn
17458 });/*
17459  * Based on:
17460  * Ext JS Library 1.1.1
17461  * Copyright(c) 2006-2007, Ext JS, LLC.
17462  *
17463  * Originally Released Under LGPL - original licence link has changed is not relivant.
17464  *
17465  * Fork - LGPL
17466  * <script type="text/javascript">
17467  */
17468  
17469 /**
17470  * @class Roo.form.TextArea
17471  * @extends Roo.form.TextField
17472  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
17473  * support for auto-sizing.
17474  * @constructor
17475  * Creates a new TextArea
17476  * @param {Object} config Configuration options
17477  */
17478 Roo.form.TextArea = function(config){
17479     Roo.form.TextArea.superclass.constructor.call(this, config);
17480     // these are provided exchanges for backwards compat
17481     // minHeight/maxHeight were replaced by growMin/growMax to be
17482     // compatible with TextField growing config values
17483     if(this.minHeight !== undefined){
17484         this.growMin = this.minHeight;
17485     }
17486     if(this.maxHeight !== undefined){
17487         this.growMax = this.maxHeight;
17488     }
17489 };
17490
17491 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
17492     /**
17493      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
17494      */
17495     growMin : 60,
17496     /**
17497      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
17498      */
17499     growMax: 1000,
17500     /**
17501      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
17502      * in the field (equivalent to setting overflow: hidden, defaults to false)
17503      */
17504     preventScrollbars: false,
17505     /**
17506      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17507      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
17508      */
17509
17510     // private
17511     onRender : function(ct, position){
17512         if(!this.el){
17513             this.defaultAutoCreate = {
17514                 tag: "textarea",
17515                 style:"width:300px;height:60px;",
17516                 autocomplete: "new-password"
17517             };
17518         }
17519         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
17520         if(this.grow){
17521             this.textSizeEl = Roo.DomHelper.append(document.body, {
17522                 tag: "pre", cls: "x-form-grow-sizer"
17523             });
17524             if(this.preventScrollbars){
17525                 this.el.setStyle("overflow", "hidden");
17526             }
17527             this.el.setHeight(this.growMin);
17528         }
17529     },
17530
17531     onDestroy : function(){
17532         if(this.textSizeEl){
17533             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
17534         }
17535         Roo.form.TextArea.superclass.onDestroy.call(this);
17536     },
17537
17538     // private
17539     onKeyUp : function(e){
17540         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
17541             this.autoSize();
17542         }
17543     },
17544
17545     /**
17546      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
17547      * This only takes effect if grow = true, and fires the autosize event if the height changes.
17548      */
17549     autoSize : function(){
17550         if(!this.grow || !this.textSizeEl){
17551             return;
17552         }
17553         var el = this.el;
17554         var v = el.dom.value;
17555         var ts = this.textSizeEl;
17556
17557         ts.innerHTML = '';
17558         ts.appendChild(document.createTextNode(v));
17559         v = ts.innerHTML;
17560
17561         Roo.fly(ts).setWidth(this.el.getWidth());
17562         if(v.length < 1){
17563             v = "&#160;&#160;";
17564         }else{
17565             if(Roo.isIE){
17566                 v = v.replace(/\n/g, '<p>&#160;</p>');
17567             }
17568             v += "&#160;\n&#160;";
17569         }
17570         ts.innerHTML = v;
17571         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
17572         if(h != this.lastHeight){
17573             this.lastHeight = h;
17574             this.el.setHeight(h);
17575             this.fireEvent("autosize", this, h);
17576         }
17577     }
17578 });/*
17579  * Based on:
17580  * Ext JS Library 1.1.1
17581  * Copyright(c) 2006-2007, Ext JS, LLC.
17582  *
17583  * Originally Released Under LGPL - original licence link has changed is not relivant.
17584  *
17585  * Fork - LGPL
17586  * <script type="text/javascript">
17587  */
17588  
17589
17590 /**
17591  * @class Roo.form.NumberField
17592  * @extends Roo.form.TextField
17593  * Numeric text field that provides automatic keystroke filtering and numeric validation.
17594  * @constructor
17595  * Creates a new NumberField
17596  * @param {Object} config Configuration options
17597  */
17598 Roo.form.NumberField = function(config){
17599     Roo.form.NumberField.superclass.constructor.call(this, config);
17600 };
17601
17602 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
17603     /**
17604      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
17605      */
17606     fieldClass: "x-form-field x-form-num-field",
17607     /**
17608      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
17609      */
17610     allowDecimals : true,
17611     /**
17612      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
17613      */
17614     decimalSeparator : ".",
17615     /**
17616      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
17617      */
17618     decimalPrecision : 2,
17619     /**
17620      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
17621      */
17622     allowNegative : true,
17623     /**
17624      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
17625      */
17626     minValue : Number.NEGATIVE_INFINITY,
17627     /**
17628      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
17629      */
17630     maxValue : Number.MAX_VALUE,
17631     /**
17632      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
17633      */
17634     minText : "The minimum value for this field is {0}",
17635     /**
17636      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
17637      */
17638     maxText : "The maximum value for this field is {0}",
17639     /**
17640      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
17641      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
17642      */
17643     nanText : "{0} is not a valid number",
17644
17645     // private
17646     initEvents : function(){
17647         Roo.form.NumberField.superclass.initEvents.call(this);
17648         var allowed = "0123456789";
17649         if(this.allowDecimals){
17650             allowed += this.decimalSeparator;
17651         }
17652         if(this.allowNegative){
17653             allowed += "-";
17654         }
17655         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
17656         var keyPress = function(e){
17657             var k = e.getKey();
17658             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
17659                 return;
17660             }
17661             var c = e.getCharCode();
17662             if(allowed.indexOf(String.fromCharCode(c)) === -1){
17663                 e.stopEvent();
17664             }
17665         };
17666         this.el.on("keypress", keyPress, this);
17667     },
17668
17669     // private
17670     validateValue : function(value){
17671         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
17672             return false;
17673         }
17674         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17675              return true;
17676         }
17677         var num = this.parseValue(value);
17678         if(isNaN(num)){
17679             this.markInvalid(String.format(this.nanText, value));
17680             return false;
17681         }
17682         if(num < this.minValue){
17683             this.markInvalid(String.format(this.minText, this.minValue));
17684             return false;
17685         }
17686         if(num > this.maxValue){
17687             this.markInvalid(String.format(this.maxText, this.maxValue));
17688             return false;
17689         }
17690         return true;
17691     },
17692
17693     getValue : function(){
17694         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
17695     },
17696
17697     // private
17698     parseValue : function(value){
17699         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
17700         return isNaN(value) ? '' : value;
17701     },
17702
17703     // private
17704     fixPrecision : function(value){
17705         var nan = isNaN(value);
17706         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
17707             return nan ? '' : value;
17708         }
17709         return parseFloat(value).toFixed(this.decimalPrecision);
17710     },
17711
17712     setValue : function(v){
17713         v = this.fixPrecision(v);
17714         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
17715     },
17716
17717     // private
17718     decimalPrecisionFcn : function(v){
17719         return Math.floor(v);
17720     },
17721
17722     beforeBlur : function(){
17723         var v = this.parseValue(this.getRawValue());
17724         if(v){
17725             this.setValue(v);
17726         }
17727     }
17728 });/*
17729  * Based on:
17730  * Ext JS Library 1.1.1
17731  * Copyright(c) 2006-2007, Ext JS, LLC.
17732  *
17733  * Originally Released Under LGPL - original licence link has changed is not relivant.
17734  *
17735  * Fork - LGPL
17736  * <script type="text/javascript">
17737  */
17738  
17739 /**
17740  * @class Roo.form.DateField
17741  * @extends Roo.form.TriggerField
17742  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17743 * @constructor
17744 * Create a new DateField
17745 * @param {Object} config
17746  */
17747 Roo.form.DateField = function(config){
17748     Roo.form.DateField.superclass.constructor.call(this, config);
17749     
17750       this.addEvents({
17751          
17752         /**
17753          * @event select
17754          * Fires when a date is selected
17755              * @param {Roo.form.DateField} combo This combo box
17756              * @param {Date} date The date selected
17757              */
17758         'select' : true
17759          
17760     });
17761     
17762     
17763     if(typeof this.minValue == "string") {
17764         this.minValue = this.parseDate(this.minValue);
17765     }
17766     if(typeof this.maxValue == "string") {
17767         this.maxValue = this.parseDate(this.maxValue);
17768     }
17769     this.ddMatch = null;
17770     if(this.disabledDates){
17771         var dd = this.disabledDates;
17772         var re = "(?:";
17773         for(var i = 0; i < dd.length; i++){
17774             re += dd[i];
17775             if(i != dd.length-1) {
17776                 re += "|";
17777             }
17778         }
17779         this.ddMatch = new RegExp(re + ")");
17780     }
17781 };
17782
17783 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
17784     /**
17785      * @cfg {String} format
17786      * The default date format string which can be overriden for localization support.  The format must be
17787      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17788      */
17789     format : "m/d/y",
17790     /**
17791      * @cfg {String} altFormats
17792      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17793      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17794      */
17795     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17796     /**
17797      * @cfg {Array} disabledDays
17798      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17799      */
17800     disabledDays : null,
17801     /**
17802      * @cfg {String} disabledDaysText
17803      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17804      */
17805     disabledDaysText : "Disabled",
17806     /**
17807      * @cfg {Array} disabledDates
17808      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17809      * expression so they are very powerful. Some examples:
17810      * <ul>
17811      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17812      * <li>["03/08", "09/16"] would disable those days for every year</li>
17813      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17814      * <li>["03/../2006"] would disable every day in March 2006</li>
17815      * <li>["^03"] would disable every day in every March</li>
17816      * </ul>
17817      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17818      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17819      */
17820     disabledDates : null,
17821     /**
17822      * @cfg {String} disabledDatesText
17823      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17824      */
17825     disabledDatesText : "Disabled",
17826     /**
17827      * @cfg {Date/String} minValue
17828      * The minimum allowed date. Can be either a Javascript date object or a string date in a
17829      * valid format (defaults to null).
17830      */
17831     minValue : null,
17832     /**
17833      * @cfg {Date/String} maxValue
17834      * The maximum allowed date. Can be either a Javascript date object or a string date in a
17835      * valid format (defaults to null).
17836      */
17837     maxValue : null,
17838     /**
17839      * @cfg {String} minText
17840      * The error text to display when the date in the cell is before minValue (defaults to
17841      * 'The date in this field must be after {minValue}').
17842      */
17843     minText : "The date in this field must be equal to or after {0}",
17844     /**
17845      * @cfg {String} maxText
17846      * The error text to display when the date in the cell is after maxValue (defaults to
17847      * 'The date in this field must be before {maxValue}').
17848      */
17849     maxText : "The date in this field must be equal to or before {0}",
17850     /**
17851      * @cfg {String} invalidText
17852      * The error text to display when the date in the field is invalid (defaults to
17853      * '{value} is not a valid date - it must be in the format {format}').
17854      */
17855     invalidText : "{0} is not a valid date - it must be in the format {1}",
17856     /**
17857      * @cfg {String} triggerClass
17858      * An additional CSS class used to style the trigger button.  The trigger will always get the
17859      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
17860      * which displays a calendar icon).
17861      */
17862     triggerClass : 'x-form-date-trigger',
17863     
17864
17865     /**
17866      * @cfg {Boolean} useIso
17867      * if enabled, then the date field will use a hidden field to store the 
17868      * real value as iso formated date. default (false)
17869      */ 
17870     useIso : false,
17871     /**
17872      * @cfg {String/Object} autoCreate
17873      * A DomHelper element spec, or true for a default element spec (defaults to
17874      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
17875      */ 
17876     // private
17877     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
17878     
17879     // private
17880     hiddenField: false,
17881     
17882     onRender : function(ct, position)
17883     {
17884         Roo.form.DateField.superclass.onRender.call(this, ct, position);
17885         if (this.useIso) {
17886             //this.el.dom.removeAttribute('name'); 
17887             Roo.log("Changing name?");
17888             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
17889             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
17890                     'before', true);
17891             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
17892             // prevent input submission
17893             this.hiddenName = this.name;
17894         }
17895             
17896             
17897     },
17898     
17899     // private
17900     validateValue : function(value)
17901     {
17902         value = this.formatDate(value);
17903         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
17904             Roo.log('super failed');
17905             return false;
17906         }
17907         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17908              return true;
17909         }
17910         var svalue = value;
17911         value = this.parseDate(value);
17912         if(!value){
17913             Roo.log('parse date failed' + svalue);
17914             this.markInvalid(String.format(this.invalidText, svalue, this.format));
17915             return false;
17916         }
17917         var time = value.getTime();
17918         if(this.minValue && time < this.minValue.getTime()){
17919             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
17920             return false;
17921         }
17922         if(this.maxValue && time > this.maxValue.getTime()){
17923             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
17924             return false;
17925         }
17926         if(this.disabledDays){
17927             var day = value.getDay();
17928             for(var i = 0; i < this.disabledDays.length; i++) {
17929                 if(day === this.disabledDays[i]){
17930                     this.markInvalid(this.disabledDaysText);
17931                     return false;
17932                 }
17933             }
17934         }
17935         var fvalue = this.formatDate(value);
17936         if(this.ddMatch && this.ddMatch.test(fvalue)){
17937             this.markInvalid(String.format(this.disabledDatesText, fvalue));
17938             return false;
17939         }
17940         return true;
17941     },
17942
17943     // private
17944     // Provides logic to override the default TriggerField.validateBlur which just returns true
17945     validateBlur : function(){
17946         return !this.menu || !this.menu.isVisible();
17947     },
17948     
17949     getName: function()
17950     {
17951         // returns hidden if it's set..
17952         if (!this.rendered) {return ''};
17953         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
17954         
17955     },
17956
17957     /**
17958      * Returns the current date value of the date field.
17959      * @return {Date} The date value
17960      */
17961     getValue : function(){
17962         
17963         return  this.hiddenField ?
17964                 this.hiddenField.value :
17965                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
17966     },
17967
17968     /**
17969      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
17970      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
17971      * (the default format used is "m/d/y").
17972      * <br />Usage:
17973      * <pre><code>
17974 //All of these calls set the same date value (May 4, 2006)
17975
17976 //Pass a date object:
17977 var dt = new Date('5/4/06');
17978 dateField.setValue(dt);
17979
17980 //Pass a date string (default format):
17981 dateField.setValue('5/4/06');
17982
17983 //Pass a date string (custom format):
17984 dateField.format = 'Y-m-d';
17985 dateField.setValue('2006-5-4');
17986 </code></pre>
17987      * @param {String/Date} date The date or valid date string
17988      */
17989     setValue : function(date){
17990         if (this.hiddenField) {
17991             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
17992         }
17993         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
17994         // make sure the value field is always stored as a date..
17995         this.value = this.parseDate(date);
17996         
17997         
17998     },
17999
18000     // private
18001     parseDate : function(value){
18002         if(!value || value instanceof Date){
18003             return value;
18004         }
18005         var v = Date.parseDate(value, this.format);
18006          if (!v && this.useIso) {
18007             v = Date.parseDate(value, 'Y-m-d');
18008         }
18009         if(!v && this.altFormats){
18010             if(!this.altFormatsArray){
18011                 this.altFormatsArray = this.altFormats.split("|");
18012             }
18013             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18014                 v = Date.parseDate(value, this.altFormatsArray[i]);
18015             }
18016         }
18017         return v;
18018     },
18019
18020     // private
18021     formatDate : function(date, fmt){
18022         return (!date || !(date instanceof Date)) ?
18023                date : date.dateFormat(fmt || this.format);
18024     },
18025
18026     // private
18027     menuListeners : {
18028         select: function(m, d){
18029             
18030             this.setValue(d);
18031             this.fireEvent('select', this, d);
18032         },
18033         show : function(){ // retain focus styling
18034             this.onFocus();
18035         },
18036         hide : function(){
18037             this.focus.defer(10, this);
18038             var ml = this.menuListeners;
18039             this.menu.un("select", ml.select,  this);
18040             this.menu.un("show", ml.show,  this);
18041             this.menu.un("hide", ml.hide,  this);
18042         }
18043     },
18044
18045     // private
18046     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
18047     onTriggerClick : function(){
18048         if(this.disabled){
18049             return;
18050         }
18051         if(this.menu == null){
18052             this.menu = new Roo.menu.DateMenu();
18053         }
18054         Roo.apply(this.menu.picker,  {
18055             showClear: this.allowBlank,
18056             minDate : this.minValue,
18057             maxDate : this.maxValue,
18058             disabledDatesRE : this.ddMatch,
18059             disabledDatesText : this.disabledDatesText,
18060             disabledDays : this.disabledDays,
18061             disabledDaysText : this.disabledDaysText,
18062             format : this.useIso ? 'Y-m-d' : this.format,
18063             minText : String.format(this.minText, this.formatDate(this.minValue)),
18064             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
18065         });
18066         this.menu.on(Roo.apply({}, this.menuListeners, {
18067             scope:this
18068         }));
18069         this.menu.picker.setValue(this.getValue() || new Date());
18070         this.menu.show(this.el, "tl-bl?");
18071     },
18072
18073     beforeBlur : function(){
18074         var v = this.parseDate(this.getRawValue());
18075         if(v){
18076             this.setValue(v);
18077         }
18078     },
18079
18080     /*@
18081      * overide
18082      * 
18083      */
18084     isDirty : function() {
18085         if(this.disabled) {
18086             return false;
18087         }
18088         
18089         if(typeof(this.startValue) === 'undefined'){
18090             return false;
18091         }
18092         
18093         return String(this.getValue()) !== String(this.startValue);
18094         
18095     }
18096 });/*
18097  * Based on:
18098  * Ext JS Library 1.1.1
18099  * Copyright(c) 2006-2007, Ext JS, LLC.
18100  *
18101  * Originally Released Under LGPL - original licence link has changed is not relivant.
18102  *
18103  * Fork - LGPL
18104  * <script type="text/javascript">
18105  */
18106  
18107 /**
18108  * @class Roo.form.MonthField
18109  * @extends Roo.form.TriggerField
18110  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
18111 * @constructor
18112 * Create a new MonthField
18113 * @param {Object} config
18114  */
18115 Roo.form.MonthField = function(config){
18116     
18117     Roo.form.MonthField.superclass.constructor.call(this, config);
18118     
18119       this.addEvents({
18120          
18121         /**
18122          * @event select
18123          * Fires when a date is selected
18124              * @param {Roo.form.MonthFieeld} combo This combo box
18125              * @param {Date} date The date selected
18126              */
18127         'select' : true
18128          
18129     });
18130     
18131     
18132     if(typeof this.minValue == "string") {
18133         this.minValue = this.parseDate(this.minValue);
18134     }
18135     if(typeof this.maxValue == "string") {
18136         this.maxValue = this.parseDate(this.maxValue);
18137     }
18138     this.ddMatch = null;
18139     if(this.disabledDates){
18140         var dd = this.disabledDates;
18141         var re = "(?:";
18142         for(var i = 0; i < dd.length; i++){
18143             re += dd[i];
18144             if(i != dd.length-1) {
18145                 re += "|";
18146             }
18147         }
18148         this.ddMatch = new RegExp(re + ")");
18149     }
18150 };
18151
18152 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
18153     /**
18154      * @cfg {String} format
18155      * The default date format string which can be overriden for localization support.  The format must be
18156      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18157      */
18158     format : "M Y",
18159     /**
18160      * @cfg {String} altFormats
18161      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18162      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18163      */
18164     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
18165     /**
18166      * @cfg {Array} disabledDays
18167      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
18168      */
18169     disabledDays : [0,1,2,3,4,5,6],
18170     /**
18171      * @cfg {String} disabledDaysText
18172      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
18173      */
18174     disabledDaysText : "Disabled",
18175     /**
18176      * @cfg {Array} disabledDates
18177      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
18178      * expression so they are very powerful. Some examples:
18179      * <ul>
18180      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
18181      * <li>["03/08", "09/16"] would disable those days for every year</li>
18182      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
18183      * <li>["03/../2006"] would disable every day in March 2006</li>
18184      * <li>["^03"] would disable every day in every March</li>
18185      * </ul>
18186      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
18187      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
18188      */
18189     disabledDates : null,
18190     /**
18191      * @cfg {String} disabledDatesText
18192      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
18193      */
18194     disabledDatesText : "Disabled",
18195     /**
18196      * @cfg {Date/String} minValue
18197      * The minimum allowed date. Can be either a Javascript date object or a string date in a
18198      * valid format (defaults to null).
18199      */
18200     minValue : null,
18201     /**
18202      * @cfg {Date/String} maxValue
18203      * The maximum allowed date. Can be either a Javascript date object or a string date in a
18204      * valid format (defaults to null).
18205      */
18206     maxValue : null,
18207     /**
18208      * @cfg {String} minText
18209      * The error text to display when the date in the cell is before minValue (defaults to
18210      * 'The date in this field must be after {minValue}').
18211      */
18212     minText : "The date in this field must be equal to or after {0}",
18213     /**
18214      * @cfg {String} maxTextf
18215      * The error text to display when the date in the cell is after maxValue (defaults to
18216      * 'The date in this field must be before {maxValue}').
18217      */
18218     maxText : "The date in this field must be equal to or before {0}",
18219     /**
18220      * @cfg {String} invalidText
18221      * The error text to display when the date in the field is invalid (defaults to
18222      * '{value} is not a valid date - it must be in the format {format}').
18223      */
18224     invalidText : "{0} is not a valid date - it must be in the format {1}",
18225     /**
18226      * @cfg {String} triggerClass
18227      * An additional CSS class used to style the trigger button.  The trigger will always get the
18228      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
18229      * which displays a calendar icon).
18230      */
18231     triggerClass : 'x-form-date-trigger',
18232     
18233
18234     /**
18235      * @cfg {Boolean} useIso
18236      * if enabled, then the date field will use a hidden field to store the 
18237      * real value as iso formated date. default (true)
18238      */ 
18239     useIso : true,
18240     /**
18241      * @cfg {String/Object} autoCreate
18242      * A DomHelper element spec, or true for a default element spec (defaults to
18243      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
18244      */ 
18245     // private
18246     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
18247     
18248     // private
18249     hiddenField: false,
18250     
18251     hideMonthPicker : false,
18252     
18253     onRender : function(ct, position)
18254     {
18255         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
18256         if (this.useIso) {
18257             this.el.dom.removeAttribute('name'); 
18258             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
18259                     'before', true);
18260             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
18261             // prevent input submission
18262             this.hiddenName = this.name;
18263         }
18264             
18265             
18266     },
18267     
18268     // private
18269     validateValue : function(value)
18270     {
18271         value = this.formatDate(value);
18272         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
18273             return false;
18274         }
18275         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
18276              return true;
18277         }
18278         var svalue = value;
18279         value = this.parseDate(value);
18280         if(!value){
18281             this.markInvalid(String.format(this.invalidText, svalue, this.format));
18282             return false;
18283         }
18284         var time = value.getTime();
18285         if(this.minValue && time < this.minValue.getTime()){
18286             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
18287             return false;
18288         }
18289         if(this.maxValue && time > this.maxValue.getTime()){
18290             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
18291             return false;
18292         }
18293         /*if(this.disabledDays){
18294             var day = value.getDay();
18295             for(var i = 0; i < this.disabledDays.length; i++) {
18296                 if(day === this.disabledDays[i]){
18297                     this.markInvalid(this.disabledDaysText);
18298                     return false;
18299                 }
18300             }
18301         }
18302         */
18303         var fvalue = this.formatDate(value);
18304         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
18305             this.markInvalid(String.format(this.disabledDatesText, fvalue));
18306             return false;
18307         }
18308         */
18309         return true;
18310     },
18311
18312     // private
18313     // Provides logic to override the default TriggerField.validateBlur which just returns true
18314     validateBlur : function(){
18315         return !this.menu || !this.menu.isVisible();
18316     },
18317
18318     /**
18319      * Returns the current date value of the date field.
18320      * @return {Date} The date value
18321      */
18322     getValue : function(){
18323         
18324         
18325         
18326         return  this.hiddenField ?
18327                 this.hiddenField.value :
18328                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
18329     },
18330
18331     /**
18332      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
18333      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
18334      * (the default format used is "m/d/y").
18335      * <br />Usage:
18336      * <pre><code>
18337 //All of these calls set the same date value (May 4, 2006)
18338
18339 //Pass a date object:
18340 var dt = new Date('5/4/06');
18341 monthField.setValue(dt);
18342
18343 //Pass a date string (default format):
18344 monthField.setValue('5/4/06');
18345
18346 //Pass a date string (custom format):
18347 monthField.format = 'Y-m-d';
18348 monthField.setValue('2006-5-4');
18349 </code></pre>
18350      * @param {String/Date} date The date or valid date string
18351      */
18352     setValue : function(date){
18353         Roo.log('month setValue' + date);
18354         // can only be first of month..
18355         
18356         var val = this.parseDate(date);
18357         
18358         if (this.hiddenField) {
18359             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
18360         }
18361         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
18362         this.value = this.parseDate(date);
18363     },
18364
18365     // private
18366     parseDate : function(value){
18367         if(!value || value instanceof Date){
18368             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
18369             return value;
18370         }
18371         var v = Date.parseDate(value, this.format);
18372         if (!v && this.useIso) {
18373             v = Date.parseDate(value, 'Y-m-d');
18374         }
18375         if (v) {
18376             // 
18377             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
18378         }
18379         
18380         
18381         if(!v && this.altFormats){
18382             if(!this.altFormatsArray){
18383                 this.altFormatsArray = this.altFormats.split("|");
18384             }
18385             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18386                 v = Date.parseDate(value, this.altFormatsArray[i]);
18387             }
18388         }
18389         return v;
18390     },
18391
18392     // private
18393     formatDate : function(date, fmt){
18394         return (!date || !(date instanceof Date)) ?
18395                date : date.dateFormat(fmt || this.format);
18396     },
18397
18398     // private
18399     menuListeners : {
18400         select: function(m, d){
18401             this.setValue(d);
18402             this.fireEvent('select', this, d);
18403         },
18404         show : function(){ // retain focus styling
18405             this.onFocus();
18406         },
18407         hide : function(){
18408             this.focus.defer(10, this);
18409             var ml = this.menuListeners;
18410             this.menu.un("select", ml.select,  this);
18411             this.menu.un("show", ml.show,  this);
18412             this.menu.un("hide", ml.hide,  this);
18413         }
18414     },
18415     // private
18416     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
18417     onTriggerClick : function(){
18418         if(this.disabled){
18419             return;
18420         }
18421         if(this.menu == null){
18422             this.menu = new Roo.menu.DateMenu();
18423            
18424         }
18425         
18426         Roo.apply(this.menu.picker,  {
18427             
18428             showClear: this.allowBlank,
18429             minDate : this.minValue,
18430             maxDate : this.maxValue,
18431             disabledDatesRE : this.ddMatch,
18432             disabledDatesText : this.disabledDatesText,
18433             
18434             format : this.useIso ? 'Y-m-d' : this.format,
18435             minText : String.format(this.minText, this.formatDate(this.minValue)),
18436             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
18437             
18438         });
18439          this.menu.on(Roo.apply({}, this.menuListeners, {
18440             scope:this
18441         }));
18442        
18443         
18444         var m = this.menu;
18445         var p = m.picker;
18446         
18447         // hide month picker get's called when we called by 'before hide';
18448         
18449         var ignorehide = true;
18450         p.hideMonthPicker  = function(disableAnim){
18451             if (ignorehide) {
18452                 return;
18453             }
18454              if(this.monthPicker){
18455                 Roo.log("hideMonthPicker called");
18456                 if(disableAnim === true){
18457                     this.monthPicker.hide();
18458                 }else{
18459                     this.monthPicker.slideOut('t', {duration:.2});
18460                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
18461                     p.fireEvent("select", this, this.value);
18462                     m.hide();
18463                 }
18464             }
18465         }
18466         
18467         Roo.log('picker set value');
18468         Roo.log(this.getValue());
18469         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
18470         m.show(this.el, 'tl-bl?');
18471         ignorehide  = false;
18472         // this will trigger hideMonthPicker..
18473         
18474         
18475         // hidden the day picker
18476         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
18477         
18478         
18479         
18480       
18481         
18482         p.showMonthPicker.defer(100, p);
18483     
18484         
18485        
18486     },
18487
18488     beforeBlur : function(){
18489         var v = this.parseDate(this.getRawValue());
18490         if(v){
18491             this.setValue(v);
18492         }
18493     }
18494
18495     /** @cfg {Boolean} grow @hide */
18496     /** @cfg {Number} growMin @hide */
18497     /** @cfg {Number} growMax @hide */
18498     /**
18499      * @hide
18500      * @method autoSize
18501      */
18502 });/*
18503  * Based on:
18504  * Ext JS Library 1.1.1
18505  * Copyright(c) 2006-2007, Ext JS, LLC.
18506  *
18507  * Originally Released Under LGPL - original licence link has changed is not relivant.
18508  *
18509  * Fork - LGPL
18510  * <script type="text/javascript">
18511  */
18512  
18513
18514 /**
18515  * @class Roo.form.ComboBox
18516  * @extends Roo.form.TriggerField
18517  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
18518  * @constructor
18519  * Create a new ComboBox.
18520  * @param {Object} config Configuration options
18521  */
18522 Roo.form.ComboBox = function(config){
18523     Roo.form.ComboBox.superclass.constructor.call(this, config);
18524     this.addEvents({
18525         /**
18526          * @event expand
18527          * Fires when the dropdown list is expanded
18528              * @param {Roo.form.ComboBox} combo This combo box
18529              */
18530         'expand' : true,
18531         /**
18532          * @event collapse
18533          * Fires when the dropdown list is collapsed
18534              * @param {Roo.form.ComboBox} combo This combo box
18535              */
18536         'collapse' : true,
18537         /**
18538          * @event beforeselect
18539          * Fires before a list item is selected. Return false to cancel the selection.
18540              * @param {Roo.form.ComboBox} combo This combo box
18541              * @param {Roo.data.Record} record The data record returned from the underlying store
18542              * @param {Number} index The index of the selected item in the dropdown list
18543              */
18544         'beforeselect' : true,
18545         /**
18546          * @event select
18547          * Fires when a list item is selected
18548              * @param {Roo.form.ComboBox} combo This combo box
18549              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
18550              * @param {Number} index The index of the selected item in the dropdown list
18551              */
18552         'select' : true,
18553         /**
18554          * @event beforequery
18555          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
18556          * The event object passed has these properties:
18557              * @param {Roo.form.ComboBox} combo This combo box
18558              * @param {String} query The query
18559              * @param {Boolean} forceAll true to force "all" query
18560              * @param {Boolean} cancel true to cancel the query
18561              * @param {Object} e The query event object
18562              */
18563         'beforequery': true,
18564          /**
18565          * @event add
18566          * Fires when the 'add' icon is pressed (add a listener to enable add button)
18567              * @param {Roo.form.ComboBox} combo This combo box
18568              */
18569         'add' : true,
18570         /**
18571          * @event edit
18572          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
18573              * @param {Roo.form.ComboBox} combo This combo box
18574              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
18575              */
18576         'edit' : true
18577         
18578         
18579     });
18580     if(this.transform){
18581         this.allowDomMove = false;
18582         var s = Roo.getDom(this.transform);
18583         if(!this.hiddenName){
18584             this.hiddenName = s.name;
18585         }
18586         if(!this.store){
18587             this.mode = 'local';
18588             var d = [], opts = s.options;
18589             for(var i = 0, len = opts.length;i < len; i++){
18590                 var o = opts[i];
18591                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
18592                 if(o.selected) {
18593                     this.value = value;
18594                 }
18595                 d.push([value, o.text]);
18596             }
18597             this.store = new Roo.data.SimpleStore({
18598                 'id': 0,
18599                 fields: ['value', 'text'],
18600                 data : d
18601             });
18602             this.valueField = 'value';
18603             this.displayField = 'text';
18604         }
18605         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
18606         if(!this.lazyRender){
18607             this.target = true;
18608             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
18609             s.parentNode.removeChild(s); // remove it
18610             this.render(this.el.parentNode);
18611         }else{
18612             s.parentNode.removeChild(s); // remove it
18613         }
18614
18615     }
18616     if (this.store) {
18617         this.store = Roo.factory(this.store, Roo.data);
18618     }
18619     
18620     this.selectedIndex = -1;
18621     if(this.mode == 'local'){
18622         if(config.queryDelay === undefined){
18623             this.queryDelay = 10;
18624         }
18625         if(config.minChars === undefined){
18626             this.minChars = 0;
18627         }
18628     }
18629 };
18630
18631 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
18632     /**
18633      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
18634      */
18635     /**
18636      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
18637      * rendering into an Roo.Editor, defaults to false)
18638      */
18639     /**
18640      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
18641      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
18642      */
18643     /**
18644      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
18645      */
18646     /**
18647      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
18648      * the dropdown list (defaults to undefined, with no header element)
18649      */
18650
18651      /**
18652      * @cfg {String/Roo.Template} tpl The template to use to render the output
18653      */
18654      
18655     // private
18656     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
18657     /**
18658      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
18659      */
18660     listWidth: undefined,
18661     /**
18662      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
18663      * mode = 'remote' or 'text' if mode = 'local')
18664      */
18665     displayField: undefined,
18666     /**
18667      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
18668      * mode = 'remote' or 'value' if mode = 'local'). 
18669      * Note: use of a valueField requires the user make a selection
18670      * in order for a value to be mapped.
18671      */
18672     valueField: undefined,
18673     
18674     
18675     /**
18676      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
18677      * field's data value (defaults to the underlying DOM element's name)
18678      */
18679     hiddenName: undefined,
18680     /**
18681      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
18682      */
18683     listClass: '',
18684     /**
18685      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
18686      */
18687     selectedClass: 'x-combo-selected',
18688     /**
18689      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
18690      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
18691      * which displays a downward arrow icon).
18692      */
18693     triggerClass : 'x-form-arrow-trigger',
18694     /**
18695      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
18696      */
18697     shadow:'sides',
18698     /**
18699      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
18700      * anchor positions (defaults to 'tl-bl')
18701      */
18702     listAlign: 'tl-bl?',
18703     /**
18704      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
18705      */
18706     maxHeight: 300,
18707     /**
18708      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
18709      * query specified by the allQuery config option (defaults to 'query')
18710      */
18711     triggerAction: 'query',
18712     /**
18713      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
18714      * (defaults to 4, does not apply if editable = false)
18715      */
18716     minChars : 4,
18717     /**
18718      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
18719      * delay (typeAheadDelay) if it matches a known value (defaults to false)
18720      */
18721     typeAhead: false,
18722     /**
18723      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
18724      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
18725      */
18726     queryDelay: 500,
18727     /**
18728      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
18729      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
18730      */
18731     pageSize: 0,
18732     /**
18733      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
18734      * when editable = true (defaults to false)
18735      */
18736     selectOnFocus:false,
18737     /**
18738      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
18739      */
18740     queryParam: 'query',
18741     /**
18742      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
18743      * when mode = 'remote' (defaults to 'Loading...')
18744      */
18745     loadingText: 'Loading...',
18746     /**
18747      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
18748      */
18749     resizable: false,
18750     /**
18751      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
18752      */
18753     handleHeight : 8,
18754     /**
18755      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
18756      * traditional select (defaults to true)
18757      */
18758     editable: true,
18759     /**
18760      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
18761      */
18762     allQuery: '',
18763     /**
18764      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
18765      */
18766     mode: 'remote',
18767     /**
18768      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
18769      * listWidth has a higher value)
18770      */
18771     minListWidth : 70,
18772     /**
18773      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
18774      * allow the user to set arbitrary text into the field (defaults to false)
18775      */
18776     forceSelection:false,
18777     /**
18778      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
18779      * if typeAhead = true (defaults to 250)
18780      */
18781     typeAheadDelay : 250,
18782     /**
18783      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
18784      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
18785      */
18786     valueNotFoundText : undefined,
18787     /**
18788      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
18789      */
18790     blockFocus : false,
18791     
18792     /**
18793      * @cfg {Boolean} disableClear Disable showing of clear button.
18794      */
18795     disableClear : false,
18796     /**
18797      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
18798      */
18799     alwaysQuery : false,
18800     
18801     //private
18802     addicon : false,
18803     editicon: false,
18804     
18805     // element that contains real text value.. (when hidden is used..)
18806      
18807     // private
18808     onRender : function(ct, position){
18809         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
18810         if(this.hiddenName){
18811             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
18812                     'before', true);
18813             this.hiddenField.value =
18814                 this.hiddenValue !== undefined ? this.hiddenValue :
18815                 this.value !== undefined ? this.value : '';
18816
18817             // prevent input submission
18818             this.el.dom.removeAttribute('name');
18819              
18820              
18821         }
18822         if(Roo.isGecko){
18823             this.el.dom.setAttribute('autocomplete', 'off');
18824         }
18825
18826         var cls = 'x-combo-list';
18827
18828         this.list = new Roo.Layer({
18829             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
18830         });
18831
18832         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
18833         this.list.setWidth(lw);
18834         this.list.swallowEvent('mousewheel');
18835         this.assetHeight = 0;
18836
18837         if(this.title){
18838             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
18839             this.assetHeight += this.header.getHeight();
18840         }
18841
18842         this.innerList = this.list.createChild({cls:cls+'-inner'});
18843         this.innerList.on('mouseover', this.onViewOver, this);
18844         this.innerList.on('mousemove', this.onViewMove, this);
18845         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18846         
18847         if(this.allowBlank && !this.pageSize && !this.disableClear){
18848             this.footer = this.list.createChild({cls:cls+'-ft'});
18849             this.pageTb = new Roo.Toolbar(this.footer);
18850            
18851         }
18852         if(this.pageSize){
18853             this.footer = this.list.createChild({cls:cls+'-ft'});
18854             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
18855                     {pageSize: this.pageSize});
18856             
18857         }
18858         
18859         if (this.pageTb && this.allowBlank && !this.disableClear) {
18860             var _this = this;
18861             this.pageTb.add(new Roo.Toolbar.Fill(), {
18862                 cls: 'x-btn-icon x-btn-clear',
18863                 text: '&#160;',
18864                 handler: function()
18865                 {
18866                     _this.collapse();
18867                     _this.clearValue();
18868                     _this.onSelect(false, -1);
18869                 }
18870             });
18871         }
18872         if (this.footer) {
18873             this.assetHeight += this.footer.getHeight();
18874         }
18875         
18876
18877         if(!this.tpl){
18878             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
18879         }
18880
18881         this.view = new Roo.View(this.innerList, this.tpl, {
18882             singleSelect:true, store: this.store, selectedClass: this.selectedClass
18883         });
18884
18885         this.view.on('click', this.onViewClick, this);
18886
18887         this.store.on('beforeload', this.onBeforeLoad, this);
18888         this.store.on('load', this.onLoad, this);
18889         this.store.on('loadexception', this.onLoadException, this);
18890
18891         if(this.resizable){
18892             this.resizer = new Roo.Resizable(this.list,  {
18893                pinned:true, handles:'se'
18894             });
18895             this.resizer.on('resize', function(r, w, h){
18896                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
18897                 this.listWidth = w;
18898                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
18899                 this.restrictHeight();
18900             }, this);
18901             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
18902         }
18903         if(!this.editable){
18904             this.editable = true;
18905             this.setEditable(false);
18906         }  
18907         
18908         
18909         if (typeof(this.events.add.listeners) != 'undefined') {
18910             
18911             this.addicon = this.wrap.createChild(
18912                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
18913        
18914             this.addicon.on('click', function(e) {
18915                 this.fireEvent('add', this);
18916             }, this);
18917         }
18918         if (typeof(this.events.edit.listeners) != 'undefined') {
18919             
18920             this.editicon = this.wrap.createChild(
18921                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
18922             if (this.addicon) {
18923                 this.editicon.setStyle('margin-left', '40px');
18924             }
18925             this.editicon.on('click', function(e) {
18926                 
18927                 // we fire even  if inothing is selected..
18928                 this.fireEvent('edit', this, this.lastData );
18929                 
18930             }, this);
18931         }
18932         
18933         
18934         
18935     },
18936
18937     // private
18938     initEvents : function(){
18939         Roo.form.ComboBox.superclass.initEvents.call(this);
18940
18941         this.keyNav = new Roo.KeyNav(this.el, {
18942             "up" : function(e){
18943                 this.inKeyMode = true;
18944                 this.selectPrev();
18945             },
18946
18947             "down" : function(e){
18948                 if(!this.isExpanded()){
18949                     this.onTriggerClick();
18950                 }else{
18951                     this.inKeyMode = true;
18952                     this.selectNext();
18953                 }
18954             },
18955
18956             "enter" : function(e){
18957                 this.onViewClick();
18958                 //return true;
18959             },
18960
18961             "esc" : function(e){
18962                 this.collapse();
18963             },
18964
18965             "tab" : function(e){
18966                 this.onViewClick(false);
18967                 this.fireEvent("specialkey", this, e);
18968                 return true;
18969             },
18970
18971             scope : this,
18972
18973             doRelay : function(foo, bar, hname){
18974                 if(hname == 'down' || this.scope.isExpanded()){
18975                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
18976                 }
18977                 return true;
18978             },
18979
18980             forceKeyDown: true
18981         });
18982         this.queryDelay = Math.max(this.queryDelay || 10,
18983                 this.mode == 'local' ? 10 : 250);
18984         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
18985         if(this.typeAhead){
18986             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
18987         }
18988         if(this.editable !== false){
18989             this.el.on("keyup", this.onKeyUp, this);
18990         }
18991         if(this.forceSelection){
18992             this.on('blur', this.doForce, this);
18993         }
18994     },
18995
18996     onDestroy : function(){
18997         if(this.view){
18998             this.view.setStore(null);
18999             this.view.el.removeAllListeners();
19000             this.view.el.remove();
19001             this.view.purgeListeners();
19002         }
19003         if(this.list){
19004             this.list.destroy();
19005         }
19006         if(this.store){
19007             this.store.un('beforeload', this.onBeforeLoad, this);
19008             this.store.un('load', this.onLoad, this);
19009             this.store.un('loadexception', this.onLoadException, this);
19010         }
19011         Roo.form.ComboBox.superclass.onDestroy.call(this);
19012     },
19013
19014     // private
19015     fireKey : function(e){
19016         if(e.isNavKeyPress() && !this.list.isVisible()){
19017             this.fireEvent("specialkey", this, e);
19018         }
19019     },
19020
19021     // private
19022     onResize: function(w, h){
19023         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
19024         
19025         if(typeof w != 'number'){
19026             // we do not handle it!?!?
19027             return;
19028         }
19029         var tw = this.trigger.getWidth();
19030         tw += this.addicon ? this.addicon.getWidth() : 0;
19031         tw += this.editicon ? this.editicon.getWidth() : 0;
19032         var x = w - tw;
19033         this.el.setWidth( this.adjustWidth('input', x));
19034             
19035         this.trigger.setStyle('left', x+'px');
19036         
19037         if(this.list && this.listWidth === undefined){
19038             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
19039             this.list.setWidth(lw);
19040             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19041         }
19042         
19043     
19044         
19045     },
19046
19047     /**
19048      * Allow or prevent the user from directly editing the field text.  If false is passed,
19049      * the user will only be able to select from the items defined in the dropdown list.  This method
19050      * is the runtime equivalent of setting the 'editable' config option at config time.
19051      * @param {Boolean} value True to allow the user to directly edit the field text
19052      */
19053     setEditable : function(value){
19054         if(value == this.editable){
19055             return;
19056         }
19057         this.editable = value;
19058         if(!value){
19059             this.el.dom.setAttribute('readOnly', true);
19060             this.el.on('mousedown', this.onTriggerClick,  this);
19061             this.el.addClass('x-combo-noedit');
19062         }else{
19063             this.el.dom.setAttribute('readOnly', false);
19064             this.el.un('mousedown', this.onTriggerClick,  this);
19065             this.el.removeClass('x-combo-noedit');
19066         }
19067     },
19068
19069     // private
19070     onBeforeLoad : function(){
19071         if(!this.hasFocus){
19072             return;
19073         }
19074         this.innerList.update(this.loadingText ?
19075                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
19076         this.restrictHeight();
19077         this.selectedIndex = -1;
19078     },
19079
19080     // private
19081     onLoad : function(){
19082         if(!this.hasFocus){
19083             return;
19084         }
19085         if(this.store.getCount() > 0){
19086             this.expand();
19087             this.restrictHeight();
19088             if(this.lastQuery == this.allQuery){
19089                 if(this.editable){
19090                     this.el.dom.select();
19091                 }
19092                 if(!this.selectByValue(this.value, true)){
19093                     this.select(0, true);
19094                 }
19095             }else{
19096                 this.selectNext();
19097                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
19098                     this.taTask.delay(this.typeAheadDelay);
19099                 }
19100             }
19101         }else{
19102             this.onEmptyResults();
19103         }
19104         //this.el.focus();
19105     },
19106     // private
19107     onLoadException : function()
19108     {
19109         this.collapse();
19110         Roo.log(this.store.reader.jsonData);
19111         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
19112             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
19113         }
19114         
19115         
19116     },
19117     // private
19118     onTypeAhead : function(){
19119         if(this.store.getCount() > 0){
19120             var r = this.store.getAt(0);
19121             var newValue = r.data[this.displayField];
19122             var len = newValue.length;
19123             var selStart = this.getRawValue().length;
19124             if(selStart != len){
19125                 this.setRawValue(newValue);
19126                 this.selectText(selStart, newValue.length);
19127             }
19128         }
19129     },
19130
19131     // private
19132     onSelect : function(record, index){
19133         if(this.fireEvent('beforeselect', this, record, index) !== false){
19134             this.setFromData(index > -1 ? record.data : false);
19135             this.collapse();
19136             this.fireEvent('select', this, record, index);
19137         }
19138     },
19139
19140     /**
19141      * Returns the currently selected field value or empty string if no value is set.
19142      * @return {String} value The selected value
19143      */
19144     getValue : function(){
19145         if(this.valueField){
19146             return typeof this.value != 'undefined' ? this.value : '';
19147         }
19148         return Roo.form.ComboBox.superclass.getValue.call(this);
19149     },
19150
19151     /**
19152      * Clears any text/value currently set in the field
19153      */
19154     clearValue : function(){
19155         if(this.hiddenField){
19156             this.hiddenField.value = '';
19157         }
19158         this.value = '';
19159         this.setRawValue('');
19160         this.lastSelectionText = '';
19161         
19162     },
19163
19164     /**
19165      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
19166      * will be displayed in the field.  If the value does not match the data value of an existing item,
19167      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
19168      * Otherwise the field will be blank (although the value will still be set).
19169      * @param {String} value The value to match
19170      */
19171     setValue : function(v){
19172         var text = v;
19173         if(this.valueField){
19174             var r = this.findRecord(this.valueField, v);
19175             if(r){
19176                 text = r.data[this.displayField];
19177             }else if(this.valueNotFoundText !== undefined){
19178                 text = this.valueNotFoundText;
19179             }
19180         }
19181         this.lastSelectionText = text;
19182         if(this.hiddenField){
19183             this.hiddenField.value = v;
19184         }
19185         Roo.form.ComboBox.superclass.setValue.call(this, text);
19186         this.value = v;
19187     },
19188     /**
19189      * @property {Object} the last set data for the element
19190      */
19191     
19192     lastData : false,
19193     /**
19194      * Sets the value of the field based on a object which is related to the record format for the store.
19195      * @param {Object} value the value to set as. or false on reset?
19196      */
19197     setFromData : function(o){
19198         var dv = ''; // display value
19199         var vv = ''; // value value..
19200         this.lastData = o;
19201         if (this.displayField) {
19202             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
19203         } else {
19204             // this is an error condition!!!
19205             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
19206         }
19207         
19208         if(this.valueField){
19209             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
19210         }
19211         if(this.hiddenField){
19212             this.hiddenField.value = vv;
19213             
19214             this.lastSelectionText = dv;
19215             Roo.form.ComboBox.superclass.setValue.call(this, dv);
19216             this.value = vv;
19217             return;
19218         }
19219         // no hidden field.. - we store the value in 'value', but still display
19220         // display field!!!!
19221         this.lastSelectionText = dv;
19222         Roo.form.ComboBox.superclass.setValue.call(this, dv);
19223         this.value = vv;
19224         
19225         
19226     },
19227     // private
19228     reset : function(){
19229         // overridden so that last data is reset..
19230         this.setValue(this.resetValue);
19231         this.clearInvalid();
19232         this.lastData = false;
19233         if (this.view) {
19234             this.view.clearSelections();
19235         }
19236     },
19237     // private
19238     findRecord : function(prop, value){
19239         var record;
19240         if(this.store.getCount() > 0){
19241             this.store.each(function(r){
19242                 if(r.data[prop] == value){
19243                     record = r;
19244                     return false;
19245                 }
19246                 return true;
19247             });
19248         }
19249         return record;
19250     },
19251     
19252     getName: function()
19253     {
19254         // returns hidden if it's set..
19255         if (!this.rendered) {return ''};
19256         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
19257         
19258     },
19259     // private
19260     onViewMove : function(e, t){
19261         this.inKeyMode = false;
19262     },
19263
19264     // private
19265     onViewOver : function(e, t){
19266         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
19267             return;
19268         }
19269         var item = this.view.findItemFromChild(t);
19270         if(item){
19271             var index = this.view.indexOf(item);
19272             this.select(index, false);
19273         }
19274     },
19275
19276     // private
19277     onViewClick : function(doFocus)
19278     {
19279         var index = this.view.getSelectedIndexes()[0];
19280         var r = this.store.getAt(index);
19281         if(r){
19282             this.onSelect(r, index);
19283         }
19284         if(doFocus !== false && !this.blockFocus){
19285             this.el.focus();
19286         }
19287     },
19288
19289     // private
19290     restrictHeight : function(){
19291         this.innerList.dom.style.height = '';
19292         var inner = this.innerList.dom;
19293         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
19294         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
19295         this.list.beginUpdate();
19296         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
19297         this.list.alignTo(this.el, this.listAlign);
19298         this.list.endUpdate();
19299     },
19300
19301     // private
19302     onEmptyResults : function(){
19303         this.collapse();
19304     },
19305
19306     /**
19307      * Returns true if the dropdown list is expanded, else false.
19308      */
19309     isExpanded : function(){
19310         return this.list.isVisible();
19311     },
19312
19313     /**
19314      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
19315      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19316      * @param {String} value The data value of the item to select
19317      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19318      * selected item if it is not currently in view (defaults to true)
19319      * @return {Boolean} True if the value matched an item in the list, else false
19320      */
19321     selectByValue : function(v, scrollIntoView){
19322         if(v !== undefined && v !== null){
19323             var r = this.findRecord(this.valueField || this.displayField, v);
19324             if(r){
19325                 this.select(this.store.indexOf(r), scrollIntoView);
19326                 return true;
19327             }
19328         }
19329         return false;
19330     },
19331
19332     /**
19333      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
19334      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19335      * @param {Number} index The zero-based index of the list item to select
19336      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19337      * selected item if it is not currently in view (defaults to true)
19338      */
19339     select : function(index, scrollIntoView){
19340         this.selectedIndex = index;
19341         this.view.select(index);
19342         if(scrollIntoView !== false){
19343             var el = this.view.getNode(index);
19344             if(el){
19345                 this.innerList.scrollChildIntoView(el, false);
19346             }
19347         }
19348     },
19349
19350     // private
19351     selectNext : function(){
19352         var ct = this.store.getCount();
19353         if(ct > 0){
19354             if(this.selectedIndex == -1){
19355                 this.select(0);
19356             }else if(this.selectedIndex < ct-1){
19357                 this.select(this.selectedIndex+1);
19358             }
19359         }
19360     },
19361
19362     // private
19363     selectPrev : function(){
19364         var ct = this.store.getCount();
19365         if(ct > 0){
19366             if(this.selectedIndex == -1){
19367                 this.select(0);
19368             }else if(this.selectedIndex != 0){
19369                 this.select(this.selectedIndex-1);
19370             }
19371         }
19372     },
19373
19374     // private
19375     onKeyUp : function(e){
19376         if(this.editable !== false && !e.isSpecialKey()){
19377             this.lastKey = e.getKey();
19378             this.dqTask.delay(this.queryDelay);
19379         }
19380     },
19381
19382     // private
19383     validateBlur : function(){
19384         return !this.list || !this.list.isVisible();   
19385     },
19386
19387     // private
19388     initQuery : function(){
19389         this.doQuery(this.getRawValue());
19390     },
19391
19392     // private
19393     doForce : function(){
19394         if(this.el.dom.value.length > 0){
19395             this.el.dom.value =
19396                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
19397              
19398         }
19399     },
19400
19401     /**
19402      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
19403      * query allowing the query action to be canceled if needed.
19404      * @param {String} query The SQL query to execute
19405      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
19406      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
19407      * saved in the current store (defaults to false)
19408      */
19409     doQuery : function(q, forceAll){
19410         if(q === undefined || q === null){
19411             q = '';
19412         }
19413         var qe = {
19414             query: q,
19415             forceAll: forceAll,
19416             combo: this,
19417             cancel:false
19418         };
19419         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
19420             return false;
19421         }
19422         q = qe.query;
19423         forceAll = qe.forceAll;
19424         if(forceAll === true || (q.length >= this.minChars)){
19425             if(this.lastQuery != q || this.alwaysQuery){
19426                 this.lastQuery = q;
19427                 if(this.mode == 'local'){
19428                     this.selectedIndex = -1;
19429                     if(forceAll){
19430                         this.store.clearFilter();
19431                     }else{
19432                         this.store.filter(this.displayField, q);
19433                     }
19434                     this.onLoad();
19435                 }else{
19436                     this.store.baseParams[this.queryParam] = q;
19437                     this.store.load({
19438                         params: this.getParams(q)
19439                     });
19440                     this.expand();
19441                 }
19442             }else{
19443                 this.selectedIndex = -1;
19444                 this.onLoad();   
19445             }
19446         }
19447     },
19448
19449     // private
19450     getParams : function(q){
19451         var p = {};
19452         //p[this.queryParam] = q;
19453         if(this.pageSize){
19454             p.start = 0;
19455             p.limit = this.pageSize;
19456         }
19457         return p;
19458     },
19459
19460     /**
19461      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
19462      */
19463     collapse : function(){
19464         if(!this.isExpanded()){
19465             return;
19466         }
19467         this.list.hide();
19468         Roo.get(document).un('mousedown', this.collapseIf, this);
19469         Roo.get(document).un('mousewheel', this.collapseIf, this);
19470         if (!this.editable) {
19471             Roo.get(document).un('keydown', this.listKeyPress, this);
19472         }
19473         this.fireEvent('collapse', this);
19474     },
19475
19476     // private
19477     collapseIf : function(e){
19478         if(!e.within(this.wrap) && !e.within(this.list)){
19479             this.collapse();
19480         }
19481     },
19482
19483     /**
19484      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
19485      */
19486     expand : function(){
19487         if(this.isExpanded() || !this.hasFocus){
19488             return;
19489         }
19490         this.list.alignTo(this.el, this.listAlign);
19491         this.list.show();
19492         Roo.get(document).on('mousedown', this.collapseIf, this);
19493         Roo.get(document).on('mousewheel', this.collapseIf, this);
19494         if (!this.editable) {
19495             Roo.get(document).on('keydown', this.listKeyPress, this);
19496         }
19497         
19498         this.fireEvent('expand', this);
19499     },
19500
19501     // private
19502     // Implements the default empty TriggerField.onTriggerClick function
19503     onTriggerClick : function(){
19504         if(this.disabled){
19505             return;
19506         }
19507         if(this.isExpanded()){
19508             this.collapse();
19509             if (!this.blockFocus) {
19510                 this.el.focus();
19511             }
19512             
19513         }else {
19514             this.hasFocus = true;
19515             if(this.triggerAction == 'all') {
19516                 this.doQuery(this.allQuery, true);
19517             } else {
19518                 this.doQuery(this.getRawValue());
19519             }
19520             if (!this.blockFocus) {
19521                 this.el.focus();
19522             }
19523         }
19524     },
19525     listKeyPress : function(e)
19526     {
19527         //Roo.log('listkeypress');
19528         // scroll to first matching element based on key pres..
19529         if (e.isSpecialKey()) {
19530             return false;
19531         }
19532         var k = String.fromCharCode(e.getKey()).toUpperCase();
19533         //Roo.log(k);
19534         var match  = false;
19535         var csel = this.view.getSelectedNodes();
19536         var cselitem = false;
19537         if (csel.length) {
19538             var ix = this.view.indexOf(csel[0]);
19539             cselitem  = this.store.getAt(ix);
19540             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
19541                 cselitem = false;
19542             }
19543             
19544         }
19545         
19546         this.store.each(function(v) { 
19547             if (cselitem) {
19548                 // start at existing selection.
19549                 if (cselitem.id == v.id) {
19550                     cselitem = false;
19551                 }
19552                 return;
19553             }
19554                 
19555             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
19556                 match = this.store.indexOf(v);
19557                 return false;
19558             }
19559         }, this);
19560         
19561         if (match === false) {
19562             return true; // no more action?
19563         }
19564         // scroll to?
19565         this.view.select(match);
19566         var sn = Roo.get(this.view.getSelectedNodes()[0]);
19567         sn.scrollIntoView(sn.dom.parentNode, false);
19568     }
19569
19570     /** 
19571     * @cfg {Boolean} grow 
19572     * @hide 
19573     */
19574     /** 
19575     * @cfg {Number} growMin 
19576     * @hide 
19577     */
19578     /** 
19579     * @cfg {Number} growMax 
19580     * @hide 
19581     */
19582     /**
19583      * @hide
19584      * @method autoSize
19585      */
19586 });/*
19587  * Copyright(c) 2010-2012, Roo J Solutions Limited
19588  *
19589  * Licence LGPL
19590  *
19591  */
19592
19593 /**
19594  * @class Roo.form.ComboBoxArray
19595  * @extends Roo.form.TextField
19596  * A facebook style adder... for lists of email / people / countries  etc...
19597  * pick multiple items from a combo box, and shows each one.
19598  *
19599  *  Fred [x]  Brian [x]  [Pick another |v]
19600  *
19601  *
19602  *  For this to work: it needs various extra information
19603  *    - normal combo problay has
19604  *      name, hiddenName
19605  *    + displayField, valueField
19606  *
19607  *    For our purpose...
19608  *
19609  *
19610  *   If we change from 'extends' to wrapping...
19611  *   
19612  *  
19613  *
19614  
19615  
19616  * @constructor
19617  * Create a new ComboBoxArray.
19618  * @param {Object} config Configuration options
19619  */
19620  
19621
19622 Roo.form.ComboBoxArray = function(config)
19623 {
19624     this.addEvents({
19625         /**
19626          * @event beforeremove
19627          * Fires before remove the value from the list
19628              * @param {Roo.form.ComboBoxArray} _self This combo box array
19629              * @param {Roo.form.ComboBoxArray.Item} item removed item
19630              */
19631         'beforeremove' : true,
19632         /**
19633          * @event remove
19634          * Fires when remove the value from the list
19635              * @param {Roo.form.ComboBoxArray} _self This combo box array
19636              * @param {Roo.form.ComboBoxArray.Item} item removed item
19637              */
19638         'remove' : true
19639         
19640         
19641     });
19642     
19643     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
19644     
19645     this.items = new Roo.util.MixedCollection(false);
19646     
19647     // construct the child combo...
19648     
19649     
19650     
19651     
19652    
19653     
19654 }
19655
19656  
19657 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
19658
19659     /**
19660      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
19661      */
19662     
19663     lastData : false,
19664     
19665     // behavies liek a hiddne field
19666     inputType:      'hidden',
19667     /**
19668      * @cfg {Number} width The width of the box that displays the selected element
19669      */ 
19670     width:          300,
19671
19672     
19673     
19674     /**
19675      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
19676      */
19677     name : false,
19678     /**
19679      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
19680      */
19681     hiddenName : false,
19682     
19683     
19684     // private the array of items that are displayed..
19685     items  : false,
19686     // private - the hidden field el.
19687     hiddenEl : false,
19688     // private - the filed el..
19689     el : false,
19690     
19691     //validateValue : function() { return true; }, // all values are ok!
19692     //onAddClick: function() { },
19693     
19694     onRender : function(ct, position) 
19695     {
19696         
19697         // create the standard hidden element
19698         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
19699         
19700         
19701         // give fake names to child combo;
19702         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
19703         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
19704         
19705         this.combo = Roo.factory(this.combo, Roo.form);
19706         this.combo.onRender(ct, position);
19707         if (typeof(this.combo.width) != 'undefined') {
19708             this.combo.onResize(this.combo.width,0);
19709         }
19710         
19711         this.combo.initEvents();
19712         
19713         // assigned so form know we need to do this..
19714         this.store          = this.combo.store;
19715         this.valueField     = this.combo.valueField;
19716         this.displayField   = this.combo.displayField ;
19717         
19718         
19719         this.combo.wrap.addClass('x-cbarray-grp');
19720         
19721         var cbwrap = this.combo.wrap.createChild(
19722             {tag: 'div', cls: 'x-cbarray-cb'},
19723             this.combo.el.dom
19724         );
19725         
19726              
19727         this.hiddenEl = this.combo.wrap.createChild({
19728             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
19729         });
19730         this.el = this.combo.wrap.createChild({
19731             tag: 'input',  type:'hidden' , name: this.name, value : ''
19732         });
19733          //   this.el.dom.removeAttribute("name");
19734         
19735         
19736         this.outerWrap = this.combo.wrap;
19737         this.wrap = cbwrap;
19738         
19739         this.outerWrap.setWidth(this.width);
19740         this.outerWrap.dom.removeChild(this.el.dom);
19741         
19742         this.wrap.dom.appendChild(this.el.dom);
19743         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
19744         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
19745         
19746         this.combo.trigger.setStyle('position','relative');
19747         this.combo.trigger.setStyle('left', '0px');
19748         this.combo.trigger.setStyle('top', '2px');
19749         
19750         this.combo.el.setStyle('vertical-align', 'text-bottom');
19751         
19752         //this.trigger.setStyle('vertical-align', 'top');
19753         
19754         // this should use the code from combo really... on('add' ....)
19755         if (this.adder) {
19756             
19757         
19758             this.adder = this.outerWrap.createChild(
19759                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
19760             var _t = this;
19761             this.adder.on('click', function(e) {
19762                 _t.fireEvent('adderclick', this, e);
19763             }, _t);
19764         }
19765         //var _t = this;
19766         //this.adder.on('click', this.onAddClick, _t);
19767         
19768         
19769         this.combo.on('select', function(cb, rec, ix) {
19770             this.addItem(rec.data);
19771             
19772             cb.setValue('');
19773             cb.el.dom.value = '';
19774             //cb.lastData = rec.data;
19775             // add to list
19776             
19777         }, this);
19778         
19779         
19780     },
19781     
19782     
19783     getName: function()
19784     {
19785         // returns hidden if it's set..
19786         if (!this.rendered) {return ''};
19787         return  this.hiddenName ? this.hiddenName : this.name;
19788         
19789     },
19790     
19791     
19792     onResize: function(w, h){
19793         
19794         return;
19795         // not sure if this is needed..
19796         //this.combo.onResize(w,h);
19797         
19798         if(typeof w != 'number'){
19799             // we do not handle it!?!?
19800             return;
19801         }
19802         var tw = this.combo.trigger.getWidth();
19803         tw += this.addicon ? this.addicon.getWidth() : 0;
19804         tw += this.editicon ? this.editicon.getWidth() : 0;
19805         var x = w - tw;
19806         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
19807             
19808         this.combo.trigger.setStyle('left', '0px');
19809         
19810         if(this.list && this.listWidth === undefined){
19811             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
19812             this.list.setWidth(lw);
19813             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19814         }
19815         
19816     
19817         
19818     },
19819     
19820     addItem: function(rec)
19821     {
19822         var valueField = this.combo.valueField;
19823         var displayField = this.combo.displayField;
19824         if (this.items.indexOfKey(rec[valueField]) > -1) {
19825             //console.log("GOT " + rec.data.id);
19826             return;
19827         }
19828         
19829         var x = new Roo.form.ComboBoxArray.Item({
19830             //id : rec[this.idField],
19831             data : rec,
19832             displayField : displayField ,
19833             tipField : displayField ,
19834             cb : this
19835         });
19836         // use the 
19837         this.items.add(rec[valueField],x);
19838         // add it before the element..
19839         this.updateHiddenEl();
19840         x.render(this.outerWrap, this.wrap.dom);
19841         // add the image handler..
19842     },
19843     
19844     updateHiddenEl : function()
19845     {
19846         this.validate();
19847         if (!this.hiddenEl) {
19848             return;
19849         }
19850         var ar = [];
19851         var idField = this.combo.valueField;
19852         
19853         this.items.each(function(f) {
19854             ar.push(f.data[idField]);
19855            
19856         });
19857         this.hiddenEl.dom.value = ar.join(',');
19858         this.validate();
19859     },
19860     
19861     reset : function()
19862     {
19863         this.items.clear();
19864         
19865         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
19866            el.remove();
19867         });
19868         
19869         this.el.dom.value = '';
19870         if (this.hiddenEl) {
19871             this.hiddenEl.dom.value = '';
19872         }
19873         
19874     },
19875     getValue: function()
19876     {
19877         return this.hiddenEl ? this.hiddenEl.dom.value : '';
19878     },
19879     setValue: function(v) // not a valid action - must use addItems..
19880     {
19881          
19882         this.reset();
19883         
19884         
19885         
19886         if (this.store.isLocal && (typeof(v) == 'string')) {
19887             // then we can use the store to find the values..
19888             // comma seperated at present.. this needs to allow JSON based encoding..
19889             this.hiddenEl.value  = v;
19890             var v_ar = [];
19891             Roo.each(v.split(','), function(k) {
19892                 Roo.log("CHECK " + this.valueField + ',' + k);
19893                 var li = this.store.query(this.valueField, k);
19894                 if (!li.length) {
19895                     return;
19896                 }
19897                 var add = {};
19898                 add[this.valueField] = k;
19899                 add[this.displayField] = li.item(0).data[this.displayField];
19900                 
19901                 this.addItem(add);
19902             }, this) 
19903              
19904         }
19905         if (typeof(v) == 'object' ) {
19906             // then let's assume it's an array of objects..
19907             Roo.each(v, function(l) {
19908                 this.addItem(l);
19909             }, this);
19910              
19911         }
19912         
19913         
19914     },
19915     setFromData: function(v)
19916     {
19917         // this recieves an object, if setValues is called.
19918         this.reset();
19919         this.el.dom.value = v[this.displayField];
19920         this.hiddenEl.dom.value = v[this.valueField];
19921         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
19922             return;
19923         }
19924         var kv = v[this.valueField];
19925         var dv = v[this.displayField];
19926         kv = typeof(kv) != 'string' ? '' : kv;
19927         dv = typeof(dv) != 'string' ? '' : dv;
19928         
19929         
19930         var keys = kv.split(',');
19931         var display = dv.split(',');
19932         for (var i = 0 ; i < keys.length; i++) {
19933             
19934             add = {};
19935             add[this.valueField] = keys[i];
19936             add[this.displayField] = display[i];
19937             this.addItem(add);
19938         }
19939       
19940         
19941     },
19942     
19943     /**
19944      * Validates the combox array value
19945      * @return {Boolean} True if the value is valid, else false
19946      */
19947     validate : function(){
19948         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
19949             this.clearInvalid();
19950             return true;
19951         }
19952         return false;
19953     },
19954     
19955     validateValue : function(value){
19956         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
19957         
19958     },
19959     
19960     /*@
19961      * overide
19962      * 
19963      */
19964     isDirty : function() {
19965         if(this.disabled) {
19966             return false;
19967         }
19968         
19969         try {
19970             var d = Roo.decode(String(this.originalValue));
19971         } catch (e) {
19972             return String(this.getValue()) !== String(this.originalValue);
19973         }
19974         
19975         var originalValue = [];
19976         
19977         for (var i = 0; i < d.length; i++){
19978             originalValue.push(d[i][this.valueField]);
19979         }
19980         
19981         return String(this.getValue()) !== String(originalValue.join(','));
19982         
19983     }
19984     
19985 });
19986
19987
19988
19989 /**
19990  * @class Roo.form.ComboBoxArray.Item
19991  * @extends Roo.BoxComponent
19992  * A selected item in the list
19993  *  Fred [x]  Brian [x]  [Pick another |v]
19994  * 
19995  * @constructor
19996  * Create a new item.
19997  * @param {Object} config Configuration options
19998  */
19999  
20000 Roo.form.ComboBoxArray.Item = function(config) {
20001     config.id = Roo.id();
20002     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
20003 }
20004
20005 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
20006     data : {},
20007     cb: false,
20008     displayField : false,
20009     tipField : false,
20010     
20011     
20012     defaultAutoCreate : {
20013         tag: 'div',
20014         cls: 'x-cbarray-item',
20015         cn : [ 
20016             { tag: 'div' },
20017             {
20018                 tag: 'img',
20019                 width:16,
20020                 height : 16,
20021                 src : Roo.BLANK_IMAGE_URL ,
20022                 align: 'center'
20023             }
20024         ]
20025         
20026     },
20027     
20028  
20029     onRender : function(ct, position)
20030     {
20031         Roo.form.Field.superclass.onRender.call(this, ct, position);
20032         
20033         if(!this.el){
20034             var cfg = this.getAutoCreate();
20035             this.el = ct.createChild(cfg, position);
20036         }
20037         
20038         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
20039         
20040         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
20041             this.cb.renderer(this.data) :
20042             String.format('{0}',this.data[this.displayField]);
20043         
20044             
20045         this.el.child('div').dom.setAttribute('qtip',
20046                         String.format('{0}',this.data[this.tipField])
20047         );
20048         
20049         this.el.child('img').on('click', this.remove, this);
20050         
20051     },
20052    
20053     remove : function()
20054     {
20055         if(this.cb.disabled){
20056             return;
20057         }
20058         
20059         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
20060             this.cb.items.remove(this);
20061             this.el.child('img').un('click', this.remove, this);
20062             this.el.remove();
20063             this.cb.updateHiddenEl();
20064
20065             this.cb.fireEvent('remove', this.cb, this);
20066         }
20067         
20068     }
20069 });/*
20070  * Based on:
20071  * Ext JS Library 1.1.1
20072  * Copyright(c) 2006-2007, Ext JS, LLC.
20073  *
20074  * Originally Released Under LGPL - original licence link has changed is not relivant.
20075  *
20076  * Fork - LGPL
20077  * <script type="text/javascript">
20078  */
20079 /**
20080  * @class Roo.form.Checkbox
20081  * @extends Roo.form.Field
20082  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
20083  * @constructor
20084  * Creates a new Checkbox
20085  * @param {Object} config Configuration options
20086  */
20087 Roo.form.Checkbox = function(config){
20088     Roo.form.Checkbox.superclass.constructor.call(this, config);
20089     this.addEvents({
20090         /**
20091          * @event check
20092          * Fires when the checkbox is checked or unchecked.
20093              * @param {Roo.form.Checkbox} this This checkbox
20094              * @param {Boolean} checked The new checked value
20095              */
20096         check : true
20097     });
20098 };
20099
20100 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
20101     /**
20102      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
20103      */
20104     focusClass : undefined,
20105     /**
20106      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
20107      */
20108     fieldClass: "x-form-field",
20109     /**
20110      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
20111      */
20112     checked: false,
20113     /**
20114      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20115      * {tag: "input", type: "checkbox", autocomplete: "off"})
20116      */
20117     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
20118     /**
20119      * @cfg {String} boxLabel The text that appears beside the checkbox
20120      */
20121     boxLabel : "",
20122     /**
20123      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
20124      */  
20125     inputValue : '1',
20126     /**
20127      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20128      */
20129      valueOff: '0', // value when not checked..
20130
20131     actionMode : 'viewEl', 
20132     //
20133     // private
20134     itemCls : 'x-menu-check-item x-form-item',
20135     groupClass : 'x-menu-group-item',
20136     inputType : 'hidden',
20137     
20138     
20139     inSetChecked: false, // check that we are not calling self...
20140     
20141     inputElement: false, // real input element?
20142     basedOn: false, // ????
20143     
20144     isFormField: true, // not sure where this is needed!!!!
20145
20146     onResize : function(){
20147         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
20148         if(!this.boxLabel){
20149             this.el.alignTo(this.wrap, 'c-c');
20150         }
20151     },
20152
20153     initEvents : function(){
20154         Roo.form.Checkbox.superclass.initEvents.call(this);
20155         this.el.on("click", this.onClick,  this);
20156         this.el.on("change", this.onClick,  this);
20157     },
20158
20159
20160     getResizeEl : function(){
20161         return this.wrap;
20162     },
20163
20164     getPositionEl : function(){
20165         return this.wrap;
20166     },
20167
20168     // private
20169     onRender : function(ct, position){
20170         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20171         /*
20172         if(this.inputValue !== undefined){
20173             this.el.dom.value = this.inputValue;
20174         }
20175         */
20176         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20177         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20178         var viewEl = this.wrap.createChild({ 
20179             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20180         this.viewEl = viewEl;   
20181         this.wrap.on('click', this.onClick,  this); 
20182         
20183         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20184         this.el.on('propertychange', this.setFromHidden,  this);  //ie
20185         
20186         
20187         
20188         if(this.boxLabel){
20189             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20190         //    viewEl.on('click', this.onClick,  this); 
20191         }
20192         //if(this.checked){
20193             this.setChecked(this.checked);
20194         //}else{
20195             //this.checked = this.el.dom;
20196         //}
20197
20198     },
20199
20200     // private
20201     initValue : Roo.emptyFn,
20202
20203     /**
20204      * Returns the checked state of the checkbox.
20205      * @return {Boolean} True if checked, else false
20206      */
20207     getValue : function(){
20208         if(this.el){
20209             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
20210         }
20211         return this.valueOff;
20212         
20213     },
20214
20215         // private
20216     onClick : function(){ 
20217         if (this.disabled) {
20218             return;
20219         }
20220         this.setChecked(!this.checked);
20221
20222         //if(this.el.dom.checked != this.checked){
20223         //    this.setValue(this.el.dom.checked);
20224        // }
20225     },
20226
20227     /**
20228      * Sets the checked state of the checkbox.
20229      * On is always based on a string comparison between inputValue and the param.
20230      * @param {Boolean/String} value - the value to set 
20231      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
20232      */
20233     setValue : function(v,suppressEvent){
20234         
20235         
20236         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
20237         //if(this.el && this.el.dom){
20238         //    this.el.dom.checked = this.checked;
20239         //    this.el.dom.defaultChecked = this.checked;
20240         //}
20241         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
20242         //this.fireEvent("check", this, this.checked);
20243     },
20244     // private..
20245     setChecked : function(state,suppressEvent)
20246     {
20247         if (this.inSetChecked) {
20248             this.checked = state;
20249             return;
20250         }
20251         
20252     
20253         if(this.wrap){
20254             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
20255         }
20256         this.checked = state;
20257         if(suppressEvent !== true){
20258             this.fireEvent('check', this, state);
20259         }
20260         this.inSetChecked = true;
20261         this.el.dom.value = state ? this.inputValue : this.valueOff;
20262         this.inSetChecked = false;
20263         
20264     },
20265     // handle setting of hidden value by some other method!!?!?
20266     setFromHidden: function()
20267     {
20268         if(!this.el){
20269             return;
20270         }
20271         //console.log("SET FROM HIDDEN");
20272         //alert('setFrom hidden');
20273         this.setValue(this.el.dom.value);
20274     },
20275     
20276     onDestroy : function()
20277     {
20278         if(this.viewEl){
20279             Roo.get(this.viewEl).remove();
20280         }
20281          
20282         Roo.form.Checkbox.superclass.onDestroy.call(this);
20283     },
20284     
20285     setBoxLabel : function(str)
20286     {
20287         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
20288     }
20289
20290 });/*
20291  * Based on:
20292  * Ext JS Library 1.1.1
20293  * Copyright(c) 2006-2007, Ext JS, LLC.
20294  *
20295  * Originally Released Under LGPL - original licence link has changed is not relivant.
20296  *
20297  * Fork - LGPL
20298  * <script type="text/javascript">
20299  */
20300  
20301 /**
20302  * @class Roo.form.Radio
20303  * @extends Roo.form.Checkbox
20304  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
20305  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
20306  * @constructor
20307  * Creates a new Radio
20308  * @param {Object} config Configuration options
20309  */
20310 Roo.form.Radio = function(){
20311     Roo.form.Radio.superclass.constructor.apply(this, arguments);
20312 };
20313 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
20314     inputType: 'radio',
20315
20316     /**
20317      * If this radio is part of a group, it will return the selected value
20318      * @return {String}
20319      */
20320     getGroupValue : function(){
20321         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
20322     },
20323     
20324     
20325     onRender : function(ct, position){
20326         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20327         
20328         if(this.inputValue !== undefined){
20329             this.el.dom.value = this.inputValue;
20330         }
20331          
20332         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20333         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20334         //var viewEl = this.wrap.createChild({ 
20335         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20336         //this.viewEl = viewEl;   
20337         //this.wrap.on('click', this.onClick,  this); 
20338         
20339         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20340         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
20341         
20342         
20343         
20344         if(this.boxLabel){
20345             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20346         //    viewEl.on('click', this.onClick,  this); 
20347         }
20348          if(this.checked){
20349             this.el.dom.checked =   'checked' ;
20350         }
20351          
20352     } 
20353     
20354     
20355 });//<script type="text/javascript">
20356
20357 /*
20358  * Based  Ext JS Library 1.1.1
20359  * Copyright(c) 2006-2007, Ext JS, LLC.
20360  * LGPL
20361  *
20362  */
20363  
20364 /**
20365  * @class Roo.HtmlEditorCore
20366  * @extends Roo.Component
20367  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20368  *
20369  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20370  */
20371
20372 Roo.HtmlEditorCore = function(config){
20373     
20374     
20375     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20376     
20377     
20378     this.addEvents({
20379         /**
20380          * @event initialize
20381          * Fires when the editor is fully initialized (including the iframe)
20382          * @param {Roo.HtmlEditorCore} this
20383          */
20384         initialize: true,
20385         /**
20386          * @event activate
20387          * Fires when the editor is first receives the focus. Any insertion must wait
20388          * until after this event.
20389          * @param {Roo.HtmlEditorCore} this
20390          */
20391         activate: true,
20392          /**
20393          * @event beforesync
20394          * Fires before the textarea is updated with content from the editor iframe. Return false
20395          * to cancel the sync.
20396          * @param {Roo.HtmlEditorCore} this
20397          * @param {String} html
20398          */
20399         beforesync: true,
20400          /**
20401          * @event beforepush
20402          * Fires before the iframe editor is updated with content from the textarea. Return false
20403          * to cancel the push.
20404          * @param {Roo.HtmlEditorCore} this
20405          * @param {String} html
20406          */
20407         beforepush: true,
20408          /**
20409          * @event sync
20410          * Fires when the textarea is updated with content from the editor iframe.
20411          * @param {Roo.HtmlEditorCore} this
20412          * @param {String} html
20413          */
20414         sync: true,
20415          /**
20416          * @event push
20417          * Fires when the iframe editor is updated with content from the textarea.
20418          * @param {Roo.HtmlEditorCore} this
20419          * @param {String} html
20420          */
20421         push: true,
20422         
20423         /**
20424          * @event editorevent
20425          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20426          * @param {Roo.HtmlEditorCore} this
20427          */
20428         editorevent: true
20429         
20430     });
20431     
20432     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20433     
20434     // defaults : white / black...
20435     this.applyBlacklists();
20436     
20437     
20438     
20439 };
20440
20441
20442 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
20443
20444
20445      /**
20446      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
20447      */
20448     
20449     owner : false,
20450     
20451      /**
20452      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20453      *                        Roo.resizable.
20454      */
20455     resizable : false,
20456      /**
20457      * @cfg {Number} height (in pixels)
20458      */   
20459     height: 300,
20460    /**
20461      * @cfg {Number} width (in pixels)
20462      */   
20463     width: 500,
20464     
20465     /**
20466      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20467      * 
20468      */
20469     stylesheets: false,
20470     
20471     // id of frame..
20472     frameId: false,
20473     
20474     // private properties
20475     validationEvent : false,
20476     deferHeight: true,
20477     initialized : false,
20478     activated : false,
20479     sourceEditMode : false,
20480     onFocus : Roo.emptyFn,
20481     iframePad:3,
20482     hideMode:'offsets',
20483     
20484     clearUp: true,
20485     
20486     // blacklist + whitelisted elements..
20487     black: false,
20488     white: false,
20489      
20490     bodyCls : '',
20491
20492     /**
20493      * Protected method that will not generally be called directly. It
20494      * is called when the editor initializes the iframe with HTML contents. Override this method if you
20495      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20496      */
20497     getDocMarkup : function(){
20498         // body styles..
20499         var st = '';
20500         
20501         // inherit styels from page...?? 
20502         if (this.stylesheets === false) {
20503             
20504             Roo.get(document.head).select('style').each(function(node) {
20505                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20506             });
20507             
20508             Roo.get(document.head).select('link').each(function(node) { 
20509                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20510             });
20511             
20512         } else if (!this.stylesheets.length) {
20513                 // simple..
20514                 st = '<style type="text/css">' +
20515                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20516                    '</style>';
20517         } else { 
20518             st = '<style type="text/css">' +
20519                     this.stylesheets +
20520                 '</style>';
20521         }
20522         
20523         st +=  '<style type="text/css">' +
20524             'IMG { cursor: pointer } ' +
20525         '</style>';
20526
20527         var cls = 'roo-htmleditor-body';
20528         
20529         if(this.bodyCls.length){
20530             cls += ' ' + this.bodyCls;
20531         }
20532         
20533         return '<html><head>' + st  +
20534             //<style type="text/css">' +
20535             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20536             //'</style>' +
20537             ' </head><body class="' +  cls + '"></body></html>';
20538     },
20539
20540     // private
20541     onRender : function(ct, position)
20542     {
20543         var _t = this;
20544         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20545         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20546         
20547         
20548         this.el.dom.style.border = '0 none';
20549         this.el.dom.setAttribute('tabIndex', -1);
20550         this.el.addClass('x-hidden hide');
20551         
20552         
20553         
20554         if(Roo.isIE){ // fix IE 1px bogus margin
20555             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20556         }
20557        
20558         
20559         this.frameId = Roo.id();
20560         
20561          
20562         
20563         var iframe = this.owner.wrap.createChild({
20564             tag: 'iframe',
20565             cls: 'form-control', // bootstrap..
20566             id: this.frameId,
20567             name: this.frameId,
20568             frameBorder : 'no',
20569             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
20570         }, this.el
20571         );
20572         
20573         
20574         this.iframe = iframe.dom;
20575
20576          this.assignDocWin();
20577         
20578         this.doc.designMode = 'on';
20579        
20580         this.doc.open();
20581         this.doc.write(this.getDocMarkup());
20582         this.doc.close();
20583
20584         
20585         var task = { // must defer to wait for browser to be ready
20586             run : function(){
20587                 //console.log("run task?" + this.doc.readyState);
20588                 this.assignDocWin();
20589                 if(this.doc.body || this.doc.readyState == 'complete'){
20590                     try {
20591                         this.doc.designMode="on";
20592                     } catch (e) {
20593                         return;
20594                     }
20595                     Roo.TaskMgr.stop(task);
20596                     this.initEditor.defer(10, this);
20597                 }
20598             },
20599             interval : 10,
20600             duration: 10000,
20601             scope: this
20602         };
20603         Roo.TaskMgr.start(task);
20604
20605     },
20606
20607     // private
20608     onResize : function(w, h)
20609     {
20610          Roo.log('resize: ' +w + ',' + h );
20611         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20612         if(!this.iframe){
20613             return;
20614         }
20615         if(typeof w == 'number'){
20616             
20617             this.iframe.style.width = w + 'px';
20618         }
20619         if(typeof h == 'number'){
20620             
20621             this.iframe.style.height = h + 'px';
20622             if(this.doc){
20623                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20624             }
20625         }
20626         
20627     },
20628
20629     /**
20630      * Toggles the editor between standard and source edit mode.
20631      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20632      */
20633     toggleSourceEdit : function(sourceEditMode){
20634         
20635         this.sourceEditMode = sourceEditMode === true;
20636         
20637         if(this.sourceEditMode){
20638  
20639             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
20640             
20641         }else{
20642             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20643             //this.iframe.className = '';
20644             this.deferFocus();
20645         }
20646         //this.setSize(this.owner.wrap.getSize());
20647         //this.fireEvent('editmodechange', this, this.sourceEditMode);
20648     },
20649
20650     
20651   
20652
20653     /**
20654      * Protected method that will not generally be called directly. If you need/want
20655      * custom HTML cleanup, this is the method you should override.
20656      * @param {String} html The HTML to be cleaned
20657      * return {String} The cleaned HTML
20658      */
20659     cleanHtml : function(html){
20660         html = String(html);
20661         if(html.length > 5){
20662             if(Roo.isSafari){ // strip safari nonsense
20663                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20664             }
20665         }
20666         if(html == '&nbsp;'){
20667             html = '';
20668         }
20669         return html;
20670     },
20671
20672     /**
20673      * HTML Editor -> Textarea
20674      * Protected method that will not generally be called directly. Syncs the contents
20675      * of the editor iframe with the textarea.
20676      */
20677     syncValue : function(){
20678         if(this.initialized){
20679             var bd = (this.doc.body || this.doc.documentElement);
20680             //this.cleanUpPaste(); -- this is done else where and causes havoc..
20681             var html = bd.innerHTML;
20682             if(Roo.isSafari){
20683                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20684                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20685                 if(m && m[1]){
20686                     html = '<div style="'+m[0]+'">' + html + '</div>';
20687                 }
20688             }
20689             html = this.cleanHtml(html);
20690             // fix up the special chars.. normaly like back quotes in word...
20691             // however we do not want to do this with chinese..
20692             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
20693                 var cc = b.charCodeAt();
20694                 if (
20695                     (cc >= 0x4E00 && cc < 0xA000 ) ||
20696                     (cc >= 0x3400 && cc < 0x4E00 ) ||
20697                     (cc >= 0xf900 && cc < 0xfb00 )
20698                 ) {
20699                         return b;
20700                 }
20701                 return "&#"+cc+";" 
20702             });
20703             if(this.owner.fireEvent('beforesync', this, html) !== false){
20704                 this.el.dom.value = html;
20705                 this.owner.fireEvent('sync', this, html);
20706             }
20707         }
20708     },
20709
20710     /**
20711      * Protected method that will not generally be called directly. Pushes the value of the textarea
20712      * into the iframe editor.
20713      */
20714     pushValue : function(){
20715         if(this.initialized){
20716             var v = this.el.dom.value.trim();
20717             
20718 //            if(v.length < 1){
20719 //                v = '&#160;';
20720 //            }
20721             
20722             if(this.owner.fireEvent('beforepush', this, v) !== false){
20723                 var d = (this.doc.body || this.doc.documentElement);
20724                 d.innerHTML = v;
20725                 this.cleanUpPaste();
20726                 this.el.dom.value = d.innerHTML;
20727                 this.owner.fireEvent('push', this, v);
20728             }
20729         }
20730     },
20731
20732     // private
20733     deferFocus : function(){
20734         this.focus.defer(10, this);
20735     },
20736
20737     // doc'ed in Field
20738     focus : function(){
20739         if(this.win && !this.sourceEditMode){
20740             this.win.focus();
20741         }else{
20742             this.el.focus();
20743         }
20744     },
20745     
20746     assignDocWin: function()
20747     {
20748         var iframe = this.iframe;
20749         
20750          if(Roo.isIE){
20751             this.doc = iframe.contentWindow.document;
20752             this.win = iframe.contentWindow;
20753         } else {
20754 //            if (!Roo.get(this.frameId)) {
20755 //                return;
20756 //            }
20757 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20758 //            this.win = Roo.get(this.frameId).dom.contentWindow;
20759             
20760             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20761                 return;
20762             }
20763             
20764             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20765             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20766         }
20767     },
20768     
20769     // private
20770     initEditor : function(){
20771         //console.log("INIT EDITOR");
20772         this.assignDocWin();
20773         
20774         
20775         
20776         this.doc.designMode="on";
20777         this.doc.open();
20778         this.doc.write(this.getDocMarkup());
20779         this.doc.close();
20780         
20781         var dbody = (this.doc.body || this.doc.documentElement);
20782         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20783         // this copies styles from the containing element into thsi one..
20784         // not sure why we need all of this..
20785         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20786         
20787         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20788         //ss['background-attachment'] = 'fixed'; // w3c
20789         dbody.bgProperties = 'fixed'; // ie
20790         //Roo.DomHelper.applyStyles(dbody, ss);
20791         Roo.EventManager.on(this.doc, {
20792             //'mousedown': this.onEditorEvent,
20793             'mouseup': this.onEditorEvent,
20794             'dblclick': this.onEditorEvent,
20795             'click': this.onEditorEvent,
20796             'keyup': this.onEditorEvent,
20797             buffer:100,
20798             scope: this
20799         });
20800         if(Roo.isGecko){
20801             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20802         }
20803         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20804             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20805         }
20806         this.initialized = true;
20807
20808         this.owner.fireEvent('initialize', this);
20809         this.pushValue();
20810     },
20811
20812     // private
20813     onDestroy : function(){
20814         
20815         
20816         
20817         if(this.rendered){
20818             
20819             //for (var i =0; i < this.toolbars.length;i++) {
20820             //    // fixme - ask toolbars for heights?
20821             //    this.toolbars[i].onDestroy();
20822            // }
20823             
20824             //this.wrap.dom.innerHTML = '';
20825             //this.wrap.remove();
20826         }
20827     },
20828
20829     // private
20830     onFirstFocus : function(){
20831         
20832         this.assignDocWin();
20833         
20834         
20835         this.activated = true;
20836          
20837     
20838         if(Roo.isGecko){ // prevent silly gecko errors
20839             this.win.focus();
20840             var s = this.win.getSelection();
20841             if(!s.focusNode || s.focusNode.nodeType != 3){
20842                 var r = s.getRangeAt(0);
20843                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
20844                 r.collapse(true);
20845                 this.deferFocus();
20846             }
20847             try{
20848                 this.execCmd('useCSS', true);
20849                 this.execCmd('styleWithCSS', false);
20850             }catch(e){}
20851         }
20852         this.owner.fireEvent('activate', this);
20853     },
20854
20855     // private
20856     adjustFont: function(btn){
20857         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
20858         //if(Roo.isSafari){ // safari
20859         //    adjust *= 2;
20860        // }
20861         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
20862         if(Roo.isSafari){ // safari
20863             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
20864             v =  (v < 10) ? 10 : v;
20865             v =  (v > 48) ? 48 : v;
20866             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
20867             
20868         }
20869         
20870         
20871         v = Math.max(1, v+adjust);
20872         
20873         this.execCmd('FontSize', v  );
20874     },
20875
20876     onEditorEvent : function(e)
20877     {
20878         this.owner.fireEvent('editorevent', this, e);
20879       //  this.updateToolbar();
20880         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
20881     },
20882
20883     insertTag : function(tg)
20884     {
20885         // could be a bit smarter... -> wrap the current selected tRoo..
20886         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
20887             
20888             range = this.createRange(this.getSelection());
20889             var wrappingNode = this.doc.createElement(tg.toLowerCase());
20890             wrappingNode.appendChild(range.extractContents());
20891             range.insertNode(wrappingNode);
20892
20893             return;
20894             
20895             
20896             
20897         }
20898         this.execCmd("formatblock",   tg);
20899         
20900     },
20901     
20902     insertText : function(txt)
20903     {
20904         
20905         
20906         var range = this.createRange();
20907         range.deleteContents();
20908                //alert(Sender.getAttribute('label'));
20909                
20910         range.insertNode(this.doc.createTextNode(txt));
20911     } ,
20912     
20913      
20914
20915     /**
20916      * Executes a Midas editor command on the editor document and performs necessary focus and
20917      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
20918      * @param {String} cmd The Midas command
20919      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20920      */
20921     relayCmd : function(cmd, value){
20922         this.win.focus();
20923         this.execCmd(cmd, value);
20924         this.owner.fireEvent('editorevent', this);
20925         //this.updateToolbar();
20926         this.owner.deferFocus();
20927     },
20928
20929     /**
20930      * Executes a Midas editor command directly on the editor document.
20931      * For visual commands, you should use {@link #relayCmd} instead.
20932      * <b>This should only be called after the editor is initialized.</b>
20933      * @param {String} cmd The Midas command
20934      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20935      */
20936     execCmd : function(cmd, value){
20937         this.doc.execCommand(cmd, false, value === undefined ? null : value);
20938         this.syncValue();
20939     },
20940  
20941  
20942    
20943     /**
20944      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
20945      * to insert tRoo.
20946      * @param {String} text | dom node.. 
20947      */
20948     insertAtCursor : function(text)
20949     {
20950         
20951         if(!this.activated){
20952             return;
20953         }
20954         /*
20955         if(Roo.isIE){
20956             this.win.focus();
20957             var r = this.doc.selection.createRange();
20958             if(r){
20959                 r.collapse(true);
20960                 r.pasteHTML(text);
20961                 this.syncValue();
20962                 this.deferFocus();
20963             
20964             }
20965             return;
20966         }
20967         */
20968         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
20969             this.win.focus();
20970             
20971             
20972             // from jquery ui (MIT licenced)
20973             var range, node;
20974             var win = this.win;
20975             
20976             if (win.getSelection && win.getSelection().getRangeAt) {
20977                 range = win.getSelection().getRangeAt(0);
20978                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
20979                 range.insertNode(node);
20980             } else if (win.document.selection && win.document.selection.createRange) {
20981                 // no firefox support
20982                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20983                 win.document.selection.createRange().pasteHTML(txt);
20984             } else {
20985                 // no firefox support
20986                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20987                 this.execCmd('InsertHTML', txt);
20988             } 
20989             
20990             this.syncValue();
20991             
20992             this.deferFocus();
20993         }
20994     },
20995  // private
20996     mozKeyPress : function(e){
20997         if(e.ctrlKey){
20998             var c = e.getCharCode(), cmd;
20999           
21000             if(c > 0){
21001                 c = String.fromCharCode(c).toLowerCase();
21002                 switch(c){
21003                     case 'b':
21004                         cmd = 'bold';
21005                         break;
21006                     case 'i':
21007                         cmd = 'italic';
21008                         break;
21009                     
21010                     case 'u':
21011                         cmd = 'underline';
21012                         break;
21013                     
21014                     case 'v':
21015                         this.cleanUpPaste.defer(100, this);
21016                         return;
21017                         
21018                 }
21019                 if(cmd){
21020                     this.win.focus();
21021                     this.execCmd(cmd);
21022                     this.deferFocus();
21023                     e.preventDefault();
21024                 }
21025                 
21026             }
21027         }
21028     },
21029
21030     // private
21031     fixKeys : function(){ // load time branching for fastest keydown performance
21032         if(Roo.isIE){
21033             return function(e){
21034                 var k = e.getKey(), r;
21035                 if(k == e.TAB){
21036                     e.stopEvent();
21037                     r = this.doc.selection.createRange();
21038                     if(r){
21039                         r.collapse(true);
21040                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21041                         this.deferFocus();
21042                     }
21043                     return;
21044                 }
21045                 
21046                 if(k == e.ENTER){
21047                     r = this.doc.selection.createRange();
21048                     if(r){
21049                         var target = r.parentElement();
21050                         if(!target || target.tagName.toLowerCase() != 'li'){
21051                             e.stopEvent();
21052                             r.pasteHTML('<br />');
21053                             r.collapse(false);
21054                             r.select();
21055                         }
21056                     }
21057                 }
21058                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21059                     this.cleanUpPaste.defer(100, this);
21060                     return;
21061                 }
21062                 
21063                 
21064             };
21065         }else if(Roo.isOpera){
21066             return function(e){
21067                 var k = e.getKey();
21068                 if(k == e.TAB){
21069                     e.stopEvent();
21070                     this.win.focus();
21071                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21072                     this.deferFocus();
21073                 }
21074                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21075                     this.cleanUpPaste.defer(100, this);
21076                     return;
21077                 }
21078                 
21079             };
21080         }else if(Roo.isSafari){
21081             return function(e){
21082                 var k = e.getKey();
21083                 
21084                 if(k == e.TAB){
21085                     e.stopEvent();
21086                     this.execCmd('InsertText','\t');
21087                     this.deferFocus();
21088                     return;
21089                 }
21090                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21091                     this.cleanUpPaste.defer(100, this);
21092                     return;
21093                 }
21094                 
21095              };
21096         }
21097     }(),
21098     
21099     getAllAncestors: function()
21100     {
21101         var p = this.getSelectedNode();
21102         var a = [];
21103         if (!p) {
21104             a.push(p); // push blank onto stack..
21105             p = this.getParentElement();
21106         }
21107         
21108         
21109         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21110             a.push(p);
21111             p = p.parentNode;
21112         }
21113         a.push(this.doc.body);
21114         return a;
21115     },
21116     lastSel : false,
21117     lastSelNode : false,
21118     
21119     
21120     getSelection : function() 
21121     {
21122         this.assignDocWin();
21123         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21124     },
21125     
21126     getSelectedNode: function() 
21127     {
21128         // this may only work on Gecko!!!
21129         
21130         // should we cache this!!!!
21131         
21132         
21133         
21134          
21135         var range = this.createRange(this.getSelection()).cloneRange();
21136         
21137         if (Roo.isIE) {
21138             var parent = range.parentElement();
21139             while (true) {
21140                 var testRange = range.duplicate();
21141                 testRange.moveToElementText(parent);
21142                 if (testRange.inRange(range)) {
21143                     break;
21144                 }
21145                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21146                     break;
21147                 }
21148                 parent = parent.parentElement;
21149             }
21150             return parent;
21151         }
21152         
21153         // is ancestor a text element.
21154         var ac =  range.commonAncestorContainer;
21155         if (ac.nodeType == 3) {
21156             ac = ac.parentNode;
21157         }
21158         
21159         var ar = ac.childNodes;
21160          
21161         var nodes = [];
21162         var other_nodes = [];
21163         var has_other_nodes = false;
21164         for (var i=0;i<ar.length;i++) {
21165             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
21166                 continue;
21167             }
21168             // fullly contained node.
21169             
21170             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21171                 nodes.push(ar[i]);
21172                 continue;
21173             }
21174             
21175             // probably selected..
21176             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21177                 other_nodes.push(ar[i]);
21178                 continue;
21179             }
21180             // outer..
21181             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
21182                 continue;
21183             }
21184             
21185             
21186             has_other_nodes = true;
21187         }
21188         if (!nodes.length && other_nodes.length) {
21189             nodes= other_nodes;
21190         }
21191         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21192             return false;
21193         }
21194         
21195         return nodes[0];
21196     },
21197     createRange: function(sel)
21198     {
21199         // this has strange effects when using with 
21200         // top toolbar - not sure if it's a great idea.
21201         //this.editor.contentWindow.focus();
21202         if (typeof sel != "undefined") {
21203             try {
21204                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21205             } catch(e) {
21206                 return this.doc.createRange();
21207             }
21208         } else {
21209             return this.doc.createRange();
21210         }
21211     },
21212     getParentElement: function()
21213     {
21214         
21215         this.assignDocWin();
21216         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21217         
21218         var range = this.createRange(sel);
21219          
21220         try {
21221             var p = range.commonAncestorContainer;
21222             while (p.nodeType == 3) { // text node
21223                 p = p.parentNode;
21224             }
21225             return p;
21226         } catch (e) {
21227             return null;
21228         }
21229     
21230     },
21231     /***
21232      *
21233      * Range intersection.. the hard stuff...
21234      *  '-1' = before
21235      *  '0' = hits..
21236      *  '1' = after.
21237      *         [ -- selected range --- ]
21238      *   [fail]                        [fail]
21239      *
21240      *    basically..
21241      *      if end is before start or  hits it. fail.
21242      *      if start is after end or hits it fail.
21243      *
21244      *   if either hits (but other is outside. - then it's not 
21245      *   
21246      *    
21247      **/
21248     
21249     
21250     // @see http://www.thismuchiknow.co.uk/?p=64.
21251     rangeIntersectsNode : function(range, node)
21252     {
21253         var nodeRange = node.ownerDocument.createRange();
21254         try {
21255             nodeRange.selectNode(node);
21256         } catch (e) {
21257             nodeRange.selectNodeContents(node);
21258         }
21259     
21260         var rangeStartRange = range.cloneRange();
21261         rangeStartRange.collapse(true);
21262     
21263         var rangeEndRange = range.cloneRange();
21264         rangeEndRange.collapse(false);
21265     
21266         var nodeStartRange = nodeRange.cloneRange();
21267         nodeStartRange.collapse(true);
21268     
21269         var nodeEndRange = nodeRange.cloneRange();
21270         nodeEndRange.collapse(false);
21271     
21272         return rangeStartRange.compareBoundaryPoints(
21273                  Range.START_TO_START, nodeEndRange) == -1 &&
21274                rangeEndRange.compareBoundaryPoints(
21275                  Range.START_TO_START, nodeStartRange) == 1;
21276         
21277          
21278     },
21279     rangeCompareNode : function(range, node)
21280     {
21281         var nodeRange = node.ownerDocument.createRange();
21282         try {
21283             nodeRange.selectNode(node);
21284         } catch (e) {
21285             nodeRange.selectNodeContents(node);
21286         }
21287         
21288         
21289         range.collapse(true);
21290     
21291         nodeRange.collapse(true);
21292      
21293         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21294         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
21295          
21296         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21297         
21298         var nodeIsBefore   =  ss == 1;
21299         var nodeIsAfter    = ee == -1;
21300         
21301         if (nodeIsBefore && nodeIsAfter) {
21302             return 0; // outer
21303         }
21304         if (!nodeIsBefore && nodeIsAfter) {
21305             return 1; //right trailed.
21306         }
21307         
21308         if (nodeIsBefore && !nodeIsAfter) {
21309             return 2;  // left trailed.
21310         }
21311         // fully contined.
21312         return 3;
21313     },
21314
21315     // private? - in a new class?
21316     cleanUpPaste :  function()
21317     {
21318         // cleans up the whole document..
21319         Roo.log('cleanuppaste');
21320         
21321         this.cleanUpChildren(this.doc.body);
21322         var clean = this.cleanWordChars(this.doc.body.innerHTML);
21323         if (clean != this.doc.body.innerHTML) {
21324             this.doc.body.innerHTML = clean;
21325         }
21326         
21327     },
21328     
21329     cleanWordChars : function(input) {// change the chars to hex code
21330         var he = Roo.HtmlEditorCore;
21331         
21332         var output = input;
21333         Roo.each(he.swapCodes, function(sw) { 
21334             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21335             
21336             output = output.replace(swapper, sw[1]);
21337         });
21338         
21339         return output;
21340     },
21341     
21342     
21343     cleanUpChildren : function (n)
21344     {
21345         if (!n.childNodes.length) {
21346             return;
21347         }
21348         for (var i = n.childNodes.length-1; i > -1 ; i--) {
21349            this.cleanUpChild(n.childNodes[i]);
21350         }
21351     },
21352     
21353     
21354         
21355     
21356     cleanUpChild : function (node)
21357     {
21358         var ed = this;
21359         //console.log(node);
21360         if (node.nodeName == "#text") {
21361             // clean up silly Windows -- stuff?
21362             return; 
21363         }
21364         if (node.nodeName == "#comment") {
21365             node.parentNode.removeChild(node);
21366             // clean up silly Windows -- stuff?
21367             return; 
21368         }
21369         var lcname = node.tagName.toLowerCase();
21370         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21371         // whitelist of tags..
21372         
21373         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21374             // remove node.
21375             node.parentNode.removeChild(node);
21376             return;
21377             
21378         }
21379         
21380         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21381         
21382         // remove <a name=....> as rendering on yahoo mailer is borked with this.
21383         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21384         
21385         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21386         //    remove_keep_children = true;
21387         //}
21388         
21389         if (remove_keep_children) {
21390             this.cleanUpChildren(node);
21391             // inserts everything just before this node...
21392             while (node.childNodes.length) {
21393                 var cn = node.childNodes[0];
21394                 node.removeChild(cn);
21395                 node.parentNode.insertBefore(cn, node);
21396             }
21397             node.parentNode.removeChild(node);
21398             return;
21399         }
21400         
21401         if (!node.attributes || !node.attributes.length) {
21402             this.cleanUpChildren(node);
21403             return;
21404         }
21405         
21406         function cleanAttr(n,v)
21407         {
21408             
21409             if (v.match(/^\./) || v.match(/^\//)) {
21410                 return;
21411             }
21412             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
21413                 return;
21414             }
21415             if (v.match(/^#/)) {
21416                 return;
21417             }
21418 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21419             node.removeAttribute(n);
21420             
21421         }
21422         
21423         var cwhite = this.cwhite;
21424         var cblack = this.cblack;
21425             
21426         function cleanStyle(n,v)
21427         {
21428             if (v.match(/expression/)) { //XSS?? should we even bother..
21429                 node.removeAttribute(n);
21430                 return;
21431             }
21432             
21433             var parts = v.split(/;/);
21434             var clean = [];
21435             
21436             Roo.each(parts, function(p) {
21437                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21438                 if (!p.length) {
21439                     return true;
21440                 }
21441                 var l = p.split(':').shift().replace(/\s+/g,'');
21442                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21443                 
21444                 if ( cwhite.length && cblack.indexOf(l) > -1) {
21445 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21446                     //node.removeAttribute(n);
21447                     return true;
21448                 }
21449                 //Roo.log()
21450                 // only allow 'c whitelisted system attributes'
21451                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
21452 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21453                     //node.removeAttribute(n);
21454                     return true;
21455                 }
21456                 
21457                 
21458                  
21459                 
21460                 clean.push(p);
21461                 return true;
21462             });
21463             if (clean.length) { 
21464                 node.setAttribute(n, clean.join(';'));
21465             } else {
21466                 node.removeAttribute(n);
21467             }
21468             
21469         }
21470         
21471         
21472         for (var i = node.attributes.length-1; i > -1 ; i--) {
21473             var a = node.attributes[i];
21474             //console.log(a);
21475             
21476             if (a.name.toLowerCase().substr(0,2)=='on')  {
21477                 node.removeAttribute(a.name);
21478                 continue;
21479             }
21480             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21481                 node.removeAttribute(a.name);
21482                 continue;
21483             }
21484             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21485                 cleanAttr(a.name,a.value); // fixme..
21486                 continue;
21487             }
21488             if (a.name == 'style') {
21489                 cleanStyle(a.name,a.value);
21490                 continue;
21491             }
21492             /// clean up MS crap..
21493             // tecnically this should be a list of valid class'es..
21494             
21495             
21496             if (a.name == 'class') {
21497                 if (a.value.match(/^Mso/)) {
21498                     node.className = '';
21499                 }
21500                 
21501                 if (a.value.match(/^body$/)) {
21502                     node.className = '';
21503                 }
21504                 continue;
21505             }
21506             
21507             // style cleanup!?
21508             // class cleanup?
21509             
21510         }
21511         
21512         
21513         this.cleanUpChildren(node);
21514         
21515         
21516     },
21517     
21518     /**
21519      * Clean up MS wordisms...
21520      */
21521     cleanWord : function(node)
21522     {
21523         
21524         
21525         if (!node) {
21526             this.cleanWord(this.doc.body);
21527             return;
21528         }
21529         if (node.nodeName == "#text") {
21530             // clean up silly Windows -- stuff?
21531             return; 
21532         }
21533         if (node.nodeName == "#comment") {
21534             node.parentNode.removeChild(node);
21535             // clean up silly Windows -- stuff?
21536             return; 
21537         }
21538         
21539         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21540             node.parentNode.removeChild(node);
21541             return;
21542         }
21543         
21544         // remove - but keep children..
21545         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
21546             while (node.childNodes.length) {
21547                 var cn = node.childNodes[0];
21548                 node.removeChild(cn);
21549                 node.parentNode.insertBefore(cn, node);
21550             }
21551             node.parentNode.removeChild(node);
21552             this.iterateChildren(node, this.cleanWord);
21553             return;
21554         }
21555         // clean styles
21556         if (node.className.length) {
21557             
21558             var cn = node.className.split(/\W+/);
21559             var cna = [];
21560             Roo.each(cn, function(cls) {
21561                 if (cls.match(/Mso[a-zA-Z]+/)) {
21562                     return;
21563                 }
21564                 cna.push(cls);
21565             });
21566             node.className = cna.length ? cna.join(' ') : '';
21567             if (!cna.length) {
21568                 node.removeAttribute("class");
21569             }
21570         }
21571         
21572         if (node.hasAttribute("lang")) {
21573             node.removeAttribute("lang");
21574         }
21575         
21576         if (node.hasAttribute("style")) {
21577             
21578             var styles = node.getAttribute("style").split(";");
21579             var nstyle = [];
21580             Roo.each(styles, function(s) {
21581                 if (!s.match(/:/)) {
21582                     return;
21583                 }
21584                 var kv = s.split(":");
21585                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21586                     return;
21587                 }
21588                 // what ever is left... we allow.
21589                 nstyle.push(s);
21590             });
21591             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21592             if (!nstyle.length) {
21593                 node.removeAttribute('style');
21594             }
21595         }
21596         this.iterateChildren(node, this.cleanWord);
21597         
21598         
21599         
21600     },
21601     /**
21602      * iterateChildren of a Node, calling fn each time, using this as the scole..
21603      * @param {DomNode} node node to iterate children of.
21604      * @param {Function} fn method of this class to call on each item.
21605      */
21606     iterateChildren : function(node, fn)
21607     {
21608         if (!node.childNodes.length) {
21609                 return;
21610         }
21611         for (var i = node.childNodes.length-1; i > -1 ; i--) {
21612            fn.call(this, node.childNodes[i])
21613         }
21614     },
21615     
21616     
21617     /**
21618      * cleanTableWidths.
21619      *
21620      * Quite often pasting from word etc.. results in tables with column and widths.
21621      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21622      *
21623      */
21624     cleanTableWidths : function(node)
21625     {
21626          
21627          
21628         if (!node) {
21629             this.cleanTableWidths(this.doc.body);
21630             return;
21631         }
21632         
21633         // ignore list...
21634         if (node.nodeName == "#text" || node.nodeName == "#comment") {
21635             return; 
21636         }
21637         Roo.log(node.tagName);
21638         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21639             this.iterateChildren(node, this.cleanTableWidths);
21640             return;
21641         }
21642         if (node.hasAttribute('width')) {
21643             node.removeAttribute('width');
21644         }
21645         
21646          
21647         if (node.hasAttribute("style")) {
21648             // pretty basic...
21649             
21650             var styles = node.getAttribute("style").split(";");
21651             var nstyle = [];
21652             Roo.each(styles, function(s) {
21653                 if (!s.match(/:/)) {
21654                     return;
21655                 }
21656                 var kv = s.split(":");
21657                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21658                     return;
21659                 }
21660                 // what ever is left... we allow.
21661                 nstyle.push(s);
21662             });
21663             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21664             if (!nstyle.length) {
21665                 node.removeAttribute('style');
21666             }
21667         }
21668         
21669         this.iterateChildren(node, this.cleanTableWidths);
21670         
21671         
21672     },
21673     
21674     
21675     
21676     
21677     domToHTML : function(currentElement, depth, nopadtext) {
21678         
21679         depth = depth || 0;
21680         nopadtext = nopadtext || false;
21681     
21682         if (!currentElement) {
21683             return this.domToHTML(this.doc.body);
21684         }
21685         
21686         //Roo.log(currentElement);
21687         var j;
21688         var allText = false;
21689         var nodeName = currentElement.nodeName;
21690         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21691         
21692         if  (nodeName == '#text') {
21693             
21694             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21695         }
21696         
21697         
21698         var ret = '';
21699         if (nodeName != 'BODY') {
21700              
21701             var i = 0;
21702             // Prints the node tagName, such as <A>, <IMG>, etc
21703             if (tagName) {
21704                 var attr = [];
21705                 for(i = 0; i < currentElement.attributes.length;i++) {
21706                     // quoting?
21707                     var aname = currentElement.attributes.item(i).name;
21708                     if (!currentElement.attributes.item(i).value.length) {
21709                         continue;
21710                     }
21711                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21712                 }
21713                 
21714                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21715             } 
21716             else {
21717                 
21718                 // eack
21719             }
21720         } else {
21721             tagName = false;
21722         }
21723         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21724             return ret;
21725         }
21726         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21727             nopadtext = true;
21728         }
21729         
21730         
21731         // Traverse the tree
21732         i = 0;
21733         var currentElementChild = currentElement.childNodes.item(i);
21734         var allText = true;
21735         var innerHTML  = '';
21736         lastnode = '';
21737         while (currentElementChild) {
21738             // Formatting code (indent the tree so it looks nice on the screen)
21739             var nopad = nopadtext;
21740             if (lastnode == 'SPAN') {
21741                 nopad  = true;
21742             }
21743             // text
21744             if  (currentElementChild.nodeName == '#text') {
21745                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21746                 toadd = nopadtext ? toadd : toadd.trim();
21747                 if (!nopad && toadd.length > 80) {
21748                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
21749                 }
21750                 innerHTML  += toadd;
21751                 
21752                 i++;
21753                 currentElementChild = currentElement.childNodes.item(i);
21754                 lastNode = '';
21755                 continue;
21756             }
21757             allText = false;
21758             
21759             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
21760                 
21761             // Recursively traverse the tree structure of the child node
21762             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
21763             lastnode = currentElementChild.nodeName;
21764             i++;
21765             currentElementChild=currentElement.childNodes.item(i);
21766         }
21767         
21768         ret += innerHTML;
21769         
21770         if (!allText) {
21771                 // The remaining code is mostly for formatting the tree
21772             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
21773         }
21774         
21775         
21776         if (tagName) {
21777             ret+= "</"+tagName+">";
21778         }
21779         return ret;
21780         
21781     },
21782         
21783     applyBlacklists : function()
21784     {
21785         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
21786         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
21787         
21788         this.white = [];
21789         this.black = [];
21790         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
21791             if (b.indexOf(tag) > -1) {
21792                 return;
21793             }
21794             this.white.push(tag);
21795             
21796         }, this);
21797         
21798         Roo.each(w, function(tag) {
21799             if (b.indexOf(tag) > -1) {
21800                 return;
21801             }
21802             if (this.white.indexOf(tag) > -1) {
21803                 return;
21804             }
21805             this.white.push(tag);
21806             
21807         }, this);
21808         
21809         
21810         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
21811             if (w.indexOf(tag) > -1) {
21812                 return;
21813             }
21814             this.black.push(tag);
21815             
21816         }, this);
21817         
21818         Roo.each(b, function(tag) {
21819             if (w.indexOf(tag) > -1) {
21820                 return;
21821             }
21822             if (this.black.indexOf(tag) > -1) {
21823                 return;
21824             }
21825             this.black.push(tag);
21826             
21827         }, this);
21828         
21829         
21830         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
21831         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
21832         
21833         this.cwhite = [];
21834         this.cblack = [];
21835         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
21836             if (b.indexOf(tag) > -1) {
21837                 return;
21838             }
21839             this.cwhite.push(tag);
21840             
21841         }, this);
21842         
21843         Roo.each(w, function(tag) {
21844             if (b.indexOf(tag) > -1) {
21845                 return;
21846             }
21847             if (this.cwhite.indexOf(tag) > -1) {
21848                 return;
21849             }
21850             this.cwhite.push(tag);
21851             
21852         }, this);
21853         
21854         
21855         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
21856             if (w.indexOf(tag) > -1) {
21857                 return;
21858             }
21859             this.cblack.push(tag);
21860             
21861         }, this);
21862         
21863         Roo.each(b, function(tag) {
21864             if (w.indexOf(tag) > -1) {
21865                 return;
21866             }
21867             if (this.cblack.indexOf(tag) > -1) {
21868                 return;
21869             }
21870             this.cblack.push(tag);
21871             
21872         }, this);
21873     },
21874     
21875     setStylesheets : function(stylesheets)
21876     {
21877         if(typeof(stylesheets) == 'string'){
21878             Roo.get(this.iframe.contentDocument.head).createChild({
21879                 tag : 'link',
21880                 rel : 'stylesheet',
21881                 type : 'text/css',
21882                 href : stylesheets
21883             });
21884             
21885             return;
21886         }
21887         var _this = this;
21888      
21889         Roo.each(stylesheets, function(s) {
21890             if(!s.length){
21891                 return;
21892             }
21893             
21894             Roo.get(_this.iframe.contentDocument.head).createChild({
21895                 tag : 'link',
21896                 rel : 'stylesheet',
21897                 type : 'text/css',
21898                 href : s
21899             });
21900         });
21901
21902         
21903     },
21904     
21905     removeStylesheets : function()
21906     {
21907         var _this = this;
21908         
21909         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
21910             s.remove();
21911         });
21912     },
21913     
21914     setStyle : function(style)
21915     {
21916         Roo.get(this.iframe.contentDocument.head).createChild({
21917             tag : 'style',
21918             type : 'text/css',
21919             html : style
21920         });
21921
21922         return;
21923     }
21924     
21925     // hide stuff that is not compatible
21926     /**
21927      * @event blur
21928      * @hide
21929      */
21930     /**
21931      * @event change
21932      * @hide
21933      */
21934     /**
21935      * @event focus
21936      * @hide
21937      */
21938     /**
21939      * @event specialkey
21940      * @hide
21941      */
21942     /**
21943      * @cfg {String} fieldClass @hide
21944      */
21945     /**
21946      * @cfg {String} focusClass @hide
21947      */
21948     /**
21949      * @cfg {String} autoCreate @hide
21950      */
21951     /**
21952      * @cfg {String} inputType @hide
21953      */
21954     /**
21955      * @cfg {String} invalidClass @hide
21956      */
21957     /**
21958      * @cfg {String} invalidText @hide
21959      */
21960     /**
21961      * @cfg {String} msgFx @hide
21962      */
21963     /**
21964      * @cfg {String} validateOnBlur @hide
21965      */
21966 });
21967
21968 Roo.HtmlEditorCore.white = [
21969         'area', 'br', 'img', 'input', 'hr', 'wbr',
21970         
21971        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
21972        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
21973        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
21974        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
21975        'table',   'ul',         'xmp', 
21976        
21977        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
21978       'thead',   'tr', 
21979      
21980       'dir', 'menu', 'ol', 'ul', 'dl',
21981        
21982       'embed',  'object'
21983 ];
21984
21985
21986 Roo.HtmlEditorCore.black = [
21987     //    'embed',  'object', // enable - backend responsiblity to clean thiese
21988         'applet', // 
21989         'base',   'basefont', 'bgsound', 'blink',  'body', 
21990         'frame',  'frameset', 'head',    'html',   'ilayer', 
21991         'iframe', 'layer',  'link',     'meta',    'object',   
21992         'script', 'style' ,'title',  'xml' // clean later..
21993 ];
21994 Roo.HtmlEditorCore.clean = [
21995     'script', 'style', 'title', 'xml'
21996 ];
21997 Roo.HtmlEditorCore.remove = [
21998     'font'
21999 ];
22000 // attributes..
22001
22002 Roo.HtmlEditorCore.ablack = [
22003     'on'
22004 ];
22005     
22006 Roo.HtmlEditorCore.aclean = [ 
22007     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22008 ];
22009
22010 // protocols..
22011 Roo.HtmlEditorCore.pwhite= [
22012         'http',  'https',  'mailto'
22013 ];
22014
22015 // white listed style attributes.
22016 Roo.HtmlEditorCore.cwhite= [
22017       //  'text-align', /// default is to allow most things..
22018       
22019          
22020 //        'font-size'//??
22021 ];
22022
22023 // black listed style attributes.
22024 Roo.HtmlEditorCore.cblack= [
22025       //  'font-size' -- this can be set by the project 
22026 ];
22027
22028
22029 Roo.HtmlEditorCore.swapCodes   =[ 
22030     [    8211, "--" ], 
22031     [    8212, "--" ], 
22032     [    8216,  "'" ],  
22033     [    8217, "'" ],  
22034     [    8220, '"' ],  
22035     [    8221, '"' ],  
22036     [    8226, "*" ],  
22037     [    8230, "..." ]
22038 ]; 
22039
22040     //<script type="text/javascript">
22041
22042 /*
22043  * Ext JS Library 1.1.1
22044  * Copyright(c) 2006-2007, Ext JS, LLC.
22045  * Licence LGPL
22046  * 
22047  */
22048  
22049  
22050 Roo.form.HtmlEditor = function(config){
22051     
22052     
22053     
22054     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
22055     
22056     if (!this.toolbars) {
22057         this.toolbars = [];
22058     }
22059     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22060     
22061     
22062 };
22063
22064 /**
22065  * @class Roo.form.HtmlEditor
22066  * @extends Roo.form.Field
22067  * Provides a lightweight HTML Editor component.
22068  *
22069  * This has been tested on Fireforx / Chrome.. IE may not be so great..
22070  * 
22071  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
22072  * supported by this editor.</b><br/><br/>
22073  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
22074  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22075  */
22076 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
22077     /**
22078      * @cfg {Boolean} clearUp
22079      */
22080     clearUp : true,
22081       /**
22082      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22083      */
22084     toolbars : false,
22085    
22086      /**
22087      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22088      *                        Roo.resizable.
22089      */
22090     resizable : false,
22091      /**
22092      * @cfg {Number} height (in pixels)
22093      */   
22094     height: 300,
22095    /**
22096      * @cfg {Number} width (in pixels)
22097      */   
22098     width: 500,
22099     
22100     /**
22101      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22102      * 
22103      */
22104     stylesheets: false,
22105     
22106     
22107      /**
22108      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
22109      * 
22110      */
22111     cblack: false,
22112     /**
22113      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
22114      * 
22115      */
22116     cwhite: false,
22117     
22118      /**
22119      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
22120      * 
22121      */
22122     black: false,
22123     /**
22124      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
22125      * 
22126      */
22127     white: false,
22128     
22129     // id of frame..
22130     frameId: false,
22131     
22132     // private properties
22133     validationEvent : false,
22134     deferHeight: true,
22135     initialized : false,
22136     activated : false,
22137     
22138     onFocus : Roo.emptyFn,
22139     iframePad:3,
22140     hideMode:'offsets',
22141     
22142     actionMode : 'container', // defaults to hiding it...
22143     
22144     defaultAutoCreate : { // modified by initCompnoent..
22145         tag: "textarea",
22146         style:"width:500px;height:300px;",
22147         autocomplete: "new-password"
22148     },
22149
22150     // private
22151     initComponent : function(){
22152         this.addEvents({
22153             /**
22154              * @event initialize
22155              * Fires when the editor is fully initialized (including the iframe)
22156              * @param {HtmlEditor} this
22157              */
22158             initialize: true,
22159             /**
22160              * @event activate
22161              * Fires when the editor is first receives the focus. Any insertion must wait
22162              * until after this event.
22163              * @param {HtmlEditor} this
22164              */
22165             activate: true,
22166              /**
22167              * @event beforesync
22168              * Fires before the textarea is updated with content from the editor iframe. Return false
22169              * to cancel the sync.
22170              * @param {HtmlEditor} this
22171              * @param {String} html
22172              */
22173             beforesync: true,
22174              /**
22175              * @event beforepush
22176              * Fires before the iframe editor is updated with content from the textarea. Return false
22177              * to cancel the push.
22178              * @param {HtmlEditor} this
22179              * @param {String} html
22180              */
22181             beforepush: true,
22182              /**
22183              * @event sync
22184              * Fires when the textarea is updated with content from the editor iframe.
22185              * @param {HtmlEditor} this
22186              * @param {String} html
22187              */
22188             sync: true,
22189              /**
22190              * @event push
22191              * Fires when the iframe editor is updated with content from the textarea.
22192              * @param {HtmlEditor} this
22193              * @param {String} html
22194              */
22195             push: true,
22196              /**
22197              * @event editmodechange
22198              * Fires when the editor switches edit modes
22199              * @param {HtmlEditor} this
22200              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22201              */
22202             editmodechange: true,
22203             /**
22204              * @event editorevent
22205              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22206              * @param {HtmlEditor} this
22207              */
22208             editorevent: true,
22209             /**
22210              * @event firstfocus
22211              * Fires when on first focus - needed by toolbars..
22212              * @param {HtmlEditor} this
22213              */
22214             firstfocus: true,
22215             /**
22216              * @event autosave
22217              * Auto save the htmlEditor value as a file into Events
22218              * @param {HtmlEditor} this
22219              */
22220             autosave: true,
22221             /**
22222              * @event savedpreview
22223              * preview the saved version of htmlEditor
22224              * @param {HtmlEditor} this
22225              */
22226             savedpreview: true,
22227             
22228             /**
22229             * @event stylesheetsclick
22230             * Fires when press the Sytlesheets button
22231             * @param {Roo.HtmlEditorCore} this
22232             */
22233             stylesheetsclick: true
22234         });
22235         this.defaultAutoCreate =  {
22236             tag: "textarea",
22237             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
22238             autocomplete: "new-password"
22239         };
22240     },
22241
22242     /**
22243      * Protected method that will not generally be called directly. It
22244      * is called when the editor creates its toolbar. Override this method if you need to
22245      * add custom toolbar buttons.
22246      * @param {HtmlEditor} editor
22247      */
22248     createToolbar : function(editor){
22249         Roo.log("create toolbars");
22250         if (!editor.toolbars || !editor.toolbars.length) {
22251             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
22252         }
22253         
22254         for (var i =0 ; i < editor.toolbars.length;i++) {
22255             editor.toolbars[i] = Roo.factory(
22256                     typeof(editor.toolbars[i]) == 'string' ?
22257                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
22258                 Roo.form.HtmlEditor);
22259             editor.toolbars[i].init(editor);
22260         }
22261          
22262         
22263     },
22264
22265      
22266     // private
22267     onRender : function(ct, position)
22268     {
22269         var _t = this;
22270         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
22271         
22272         this.wrap = this.el.wrap({
22273             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22274         });
22275         
22276         this.editorcore.onRender(ct, position);
22277          
22278         if (this.resizable) {
22279             this.resizeEl = new Roo.Resizable(this.wrap, {
22280                 pinned : true,
22281                 wrap: true,
22282                 dynamic : true,
22283                 minHeight : this.height,
22284                 height: this.height,
22285                 handles : this.resizable,
22286                 width: this.width,
22287                 listeners : {
22288                     resize : function(r, w, h) {
22289                         _t.onResize(w,h); // -something
22290                     }
22291                 }
22292             });
22293             
22294         }
22295         this.createToolbar(this);
22296        
22297         
22298         if(!this.width){
22299             this.setSize(this.wrap.getSize());
22300         }
22301         if (this.resizeEl) {
22302             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22303             // should trigger onReize..
22304         }
22305         
22306         this.keyNav = new Roo.KeyNav(this.el, {
22307             
22308             "tab" : function(e){
22309                 e.preventDefault();
22310                 
22311                 var value = this.getValue();
22312                 
22313                 var start = this.el.dom.selectionStart;
22314                 var end = this.el.dom.selectionEnd;
22315                 
22316                 if(!e.shiftKey){
22317                     
22318                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
22319                     this.el.dom.setSelectionRange(end + 1, end + 1);
22320                     return;
22321                 }
22322                 
22323                 var f = value.substring(0, start).split("\t");
22324                 
22325                 if(f.pop().length != 0){
22326                     return;
22327                 }
22328                 
22329                 this.setValue(f.join("\t") + value.substring(end));
22330                 this.el.dom.setSelectionRange(start - 1, start - 1);
22331                 
22332             },
22333             
22334             "home" : function(e){
22335                 e.preventDefault();
22336                 
22337                 var curr = this.el.dom.selectionStart;
22338                 var lines = this.getValue().split("\n");
22339                 
22340                 if(!lines.length){
22341                     return;
22342                 }
22343                 
22344                 if(e.ctrlKey){
22345                     this.el.dom.setSelectionRange(0, 0);
22346                     return;
22347                 }
22348                 
22349                 var pos = 0;
22350                 
22351                 for (var i = 0; i < lines.length;i++) {
22352                     pos += lines[i].length;
22353                     
22354                     if(i != 0){
22355                         pos += 1;
22356                     }
22357                     
22358                     if(pos < curr){
22359                         continue;
22360                     }
22361                     
22362                     pos -= lines[i].length;
22363                     
22364                     break;
22365                 }
22366                 
22367                 if(!e.shiftKey){
22368                     this.el.dom.setSelectionRange(pos, pos);
22369                     return;
22370                 }
22371                 
22372                 this.el.dom.selectionStart = pos;
22373                 this.el.dom.selectionEnd = curr;
22374             },
22375             
22376             "end" : function(e){
22377                 e.preventDefault();
22378                 
22379                 var curr = this.el.dom.selectionStart;
22380                 var lines = this.getValue().split("\n");
22381                 
22382                 if(!lines.length){
22383                     return;
22384                 }
22385                 
22386                 if(e.ctrlKey){
22387                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
22388                     return;
22389                 }
22390                 
22391                 var pos = 0;
22392                 
22393                 for (var i = 0; i < lines.length;i++) {
22394                     
22395                     pos += lines[i].length;
22396                     
22397                     if(i != 0){
22398                         pos += 1;
22399                     }
22400                     
22401                     if(pos < curr){
22402                         continue;
22403                     }
22404                     
22405                     break;
22406                 }
22407                 
22408                 if(!e.shiftKey){
22409                     this.el.dom.setSelectionRange(pos, pos);
22410                     return;
22411                 }
22412                 
22413                 this.el.dom.selectionStart = curr;
22414                 this.el.dom.selectionEnd = pos;
22415             },
22416
22417             scope : this,
22418
22419             doRelay : function(foo, bar, hname){
22420                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
22421             },
22422
22423             forceKeyDown: true
22424         });
22425         
22426 //        if(this.autosave && this.w){
22427 //            this.autoSaveFn = setInterval(this.autosave, 1000);
22428 //        }
22429     },
22430
22431     // private
22432     onResize : function(w, h)
22433     {
22434         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
22435         var ew = false;
22436         var eh = false;
22437         
22438         if(this.el ){
22439             if(typeof w == 'number'){
22440                 var aw = w - this.wrap.getFrameWidth('lr');
22441                 this.el.setWidth(this.adjustWidth('textarea', aw));
22442                 ew = aw;
22443             }
22444             if(typeof h == 'number'){
22445                 var tbh = 0;
22446                 for (var i =0; i < this.toolbars.length;i++) {
22447                     // fixme - ask toolbars for heights?
22448                     tbh += this.toolbars[i].tb.el.getHeight();
22449                     if (this.toolbars[i].footer) {
22450                         tbh += this.toolbars[i].footer.el.getHeight();
22451                     }
22452                 }
22453                 
22454                 
22455                 
22456                 
22457                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
22458                 ah -= 5; // knock a few pixes off for look..
22459 //                Roo.log(ah);
22460                 this.el.setHeight(this.adjustWidth('textarea', ah));
22461                 var eh = ah;
22462             }
22463         }
22464         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
22465         this.editorcore.onResize(ew,eh);
22466         
22467     },
22468
22469     /**
22470      * Toggles the editor between standard and source edit mode.
22471      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22472      */
22473     toggleSourceEdit : function(sourceEditMode)
22474     {
22475         this.editorcore.toggleSourceEdit(sourceEditMode);
22476         
22477         if(this.editorcore.sourceEditMode){
22478             Roo.log('editor - showing textarea');
22479             
22480 //            Roo.log('in');
22481 //            Roo.log(this.syncValue());
22482             this.editorcore.syncValue();
22483             this.el.removeClass('x-hidden');
22484             this.el.dom.removeAttribute('tabIndex');
22485             this.el.focus();
22486             
22487             for (var i = 0; i < this.toolbars.length; i++) {
22488                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
22489                     this.toolbars[i].tb.hide();
22490                     this.toolbars[i].footer.hide();
22491                 }
22492             }
22493             
22494         }else{
22495             Roo.log('editor - hiding textarea');
22496 //            Roo.log('out')
22497 //            Roo.log(this.pushValue()); 
22498             this.editorcore.pushValue();
22499             
22500             this.el.addClass('x-hidden');
22501             this.el.dom.setAttribute('tabIndex', -1);
22502             
22503             for (var i = 0; i < this.toolbars.length; i++) {
22504                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
22505                     this.toolbars[i].tb.show();
22506                     this.toolbars[i].footer.show();
22507                 }
22508             }
22509             
22510             //this.deferFocus();
22511         }
22512         
22513         this.setSize(this.wrap.getSize());
22514         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
22515         
22516         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
22517     },
22518  
22519     // private (for BoxComponent)
22520     adjustSize : Roo.BoxComponent.prototype.adjustSize,
22521
22522     // private (for BoxComponent)
22523     getResizeEl : function(){
22524         return this.wrap;
22525     },
22526
22527     // private (for BoxComponent)
22528     getPositionEl : function(){
22529         return this.wrap;
22530     },
22531
22532     // private
22533     initEvents : function(){
22534         this.originalValue = this.getValue();
22535     },
22536
22537     /**
22538      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22539      * @method
22540      */
22541     markInvalid : Roo.emptyFn,
22542     /**
22543      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22544      * @method
22545      */
22546     clearInvalid : Roo.emptyFn,
22547
22548     setValue : function(v){
22549         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
22550         this.editorcore.pushValue();
22551     },
22552
22553      
22554     // private
22555     deferFocus : function(){
22556         this.focus.defer(10, this);
22557     },
22558
22559     // doc'ed in Field
22560     focus : function(){
22561         this.editorcore.focus();
22562         
22563     },
22564       
22565
22566     // private
22567     onDestroy : function(){
22568         
22569         
22570         
22571         if(this.rendered){
22572             
22573             for (var i =0; i < this.toolbars.length;i++) {
22574                 // fixme - ask toolbars for heights?
22575                 this.toolbars[i].onDestroy();
22576             }
22577             
22578             this.wrap.dom.innerHTML = '';
22579             this.wrap.remove();
22580         }
22581     },
22582
22583     // private
22584     onFirstFocus : function(){
22585         //Roo.log("onFirstFocus");
22586         this.editorcore.onFirstFocus();
22587          for (var i =0; i < this.toolbars.length;i++) {
22588             this.toolbars[i].onFirstFocus();
22589         }
22590         
22591     },
22592     
22593     // private
22594     syncValue : function()
22595     {
22596         this.editorcore.syncValue();
22597     },
22598     
22599     pushValue : function()
22600     {
22601         this.editorcore.pushValue();
22602     },
22603     
22604     setStylesheets : function(stylesheets)
22605     {
22606         this.editorcore.setStylesheets(stylesheets);
22607     },
22608     
22609     removeStylesheets : function()
22610     {
22611         this.editorcore.removeStylesheets();
22612     }
22613      
22614     
22615     // hide stuff that is not compatible
22616     /**
22617      * @event blur
22618      * @hide
22619      */
22620     /**
22621      * @event change
22622      * @hide
22623      */
22624     /**
22625      * @event focus
22626      * @hide
22627      */
22628     /**
22629      * @event specialkey
22630      * @hide
22631      */
22632     /**
22633      * @cfg {String} fieldClass @hide
22634      */
22635     /**
22636      * @cfg {String} focusClass @hide
22637      */
22638     /**
22639      * @cfg {String} autoCreate @hide
22640      */
22641     /**
22642      * @cfg {String} inputType @hide
22643      */
22644     /**
22645      * @cfg {String} invalidClass @hide
22646      */
22647     /**
22648      * @cfg {String} invalidText @hide
22649      */
22650     /**
22651      * @cfg {String} msgFx @hide
22652      */
22653     /**
22654      * @cfg {String} validateOnBlur @hide
22655      */
22656 });
22657  
22658     // <script type="text/javascript">
22659 /*
22660  * Based on
22661  * Ext JS Library 1.1.1
22662  * Copyright(c) 2006-2007, Ext JS, LLC.
22663  *  
22664  
22665  */
22666
22667 /**
22668  * @class Roo.form.HtmlEditorToolbar1
22669  * Basic Toolbar
22670  * 
22671  * Usage:
22672  *
22673  new Roo.form.HtmlEditor({
22674     ....
22675     toolbars : [
22676         new Roo.form.HtmlEditorToolbar1({
22677             disable : { fonts: 1 , format: 1, ..., ... , ...],
22678             btns : [ .... ]
22679         })
22680     }
22681      
22682  * 
22683  * @cfg {Object} disable List of elements to disable..
22684  * @cfg {Array} btns List of additional buttons.
22685  * 
22686  * 
22687  * NEEDS Extra CSS? 
22688  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22689  */
22690  
22691 Roo.form.HtmlEditor.ToolbarStandard = function(config)
22692 {
22693     
22694     Roo.apply(this, config);
22695     
22696     // default disabled, based on 'good practice'..
22697     this.disable = this.disable || {};
22698     Roo.applyIf(this.disable, {
22699         fontSize : true,
22700         colors : true,
22701         specialElements : true
22702     });
22703     
22704     
22705     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22706     // dont call parent... till later.
22707 }
22708
22709 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
22710     
22711     tb: false,
22712     
22713     rendered: false,
22714     
22715     editor : false,
22716     editorcore : false,
22717     /**
22718      * @cfg {Object} disable  List of toolbar elements to disable
22719          
22720      */
22721     disable : false,
22722     
22723     
22724      /**
22725      * @cfg {String} createLinkText The default text for the create link prompt
22726      */
22727     createLinkText : 'Please enter the URL for the link:',
22728     /**
22729      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
22730      */
22731     defaultLinkValue : 'http:/'+'/',
22732    
22733     
22734       /**
22735      * @cfg {Array} fontFamilies An array of available font families
22736      */
22737     fontFamilies : [
22738         'Arial',
22739         'Courier New',
22740         'Tahoma',
22741         'Times New Roman',
22742         'Verdana'
22743     ],
22744     
22745     specialChars : [
22746            "&#169;",
22747           "&#174;",     
22748           "&#8482;",    
22749           "&#163;" ,    
22750          // "&#8212;",    
22751           "&#8230;",    
22752           "&#247;" ,    
22753         //  "&#225;" ,     ?? a acute?
22754            "&#8364;"    , //Euro
22755        //   "&#8220;"    ,
22756         //  "&#8221;"    ,
22757         //  "&#8226;"    ,
22758           "&#176;"  //   , // degrees
22759
22760          // "&#233;"     , // e ecute
22761          // "&#250;"     , // u ecute?
22762     ],
22763     
22764     specialElements : [
22765         {
22766             text: "Insert Table",
22767             xtype: 'MenuItem',
22768             xns : Roo.Menu,
22769             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
22770                 
22771         },
22772         {    
22773             text: "Insert Image",
22774             xtype: 'MenuItem',
22775             xns : Roo.Menu,
22776             ihtml : '<img src="about:blank"/>'
22777             
22778         }
22779         
22780          
22781     ],
22782     
22783     
22784     inputElements : [ 
22785             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
22786             "input:submit", "input:button", "select", "textarea", "label" ],
22787     formats : [
22788         ["p"] ,  
22789         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
22790         ["pre"],[ "code"], 
22791         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
22792         ['div'],['span']
22793     ],
22794     
22795     cleanStyles : [
22796         "font-size"
22797     ],
22798      /**
22799      * @cfg {String} defaultFont default font to use.
22800      */
22801     defaultFont: 'tahoma',
22802    
22803     fontSelect : false,
22804     
22805     
22806     formatCombo : false,
22807     
22808     init : function(editor)
22809     {
22810         this.editor = editor;
22811         this.editorcore = editor.editorcore ? editor.editorcore : editor;
22812         var editorcore = this.editorcore;
22813         
22814         var _t = this;
22815         
22816         var fid = editorcore.frameId;
22817         var etb = this;
22818         function btn(id, toggle, handler){
22819             var xid = fid + '-'+ id ;
22820             return {
22821                 id : xid,
22822                 cmd : id,
22823                 cls : 'x-btn-icon x-edit-'+id,
22824                 enableToggle:toggle !== false,
22825                 scope: _t, // was editor...
22826                 handler:handler||_t.relayBtnCmd,
22827                 clickEvent:'mousedown',
22828                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
22829                 tabIndex:-1
22830             };
22831         }
22832         
22833         
22834         
22835         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
22836         this.tb = tb;
22837          // stop form submits
22838         tb.el.on('click', function(e){
22839             e.preventDefault(); // what does this do?
22840         });
22841
22842         if(!this.disable.font) { // && !Roo.isSafari){
22843             /* why no safari for fonts 
22844             editor.fontSelect = tb.el.createChild({
22845                 tag:'select',
22846                 tabIndex: -1,
22847                 cls:'x-font-select',
22848                 html: this.createFontOptions()
22849             });
22850             
22851             editor.fontSelect.on('change', function(){
22852                 var font = editor.fontSelect.dom.value;
22853                 editor.relayCmd('fontname', font);
22854                 editor.deferFocus();
22855             }, editor);
22856             
22857             tb.add(
22858                 editor.fontSelect.dom,
22859                 '-'
22860             );
22861             */
22862             
22863         };
22864         if(!this.disable.formats){
22865             this.formatCombo = new Roo.form.ComboBox({
22866                 store: new Roo.data.SimpleStore({
22867                     id : 'tag',
22868                     fields: ['tag'],
22869                     data : this.formats // from states.js
22870                 }),
22871                 blockFocus : true,
22872                 name : '',
22873                 //autoCreate : {tag: "div",  size: "20"},
22874                 displayField:'tag',
22875                 typeAhead: false,
22876                 mode: 'local',
22877                 editable : false,
22878                 triggerAction: 'all',
22879                 emptyText:'Add tag',
22880                 selectOnFocus:true,
22881                 width:135,
22882                 listeners : {
22883                     'select': function(c, r, i) {
22884                         editorcore.insertTag(r.get('tag'));
22885                         editor.focus();
22886                     }
22887                 }
22888
22889             });
22890             tb.addField(this.formatCombo);
22891             
22892         }
22893         
22894         if(!this.disable.format){
22895             tb.add(
22896                 btn('bold'),
22897                 btn('italic'),
22898                 btn('underline'),
22899                 btn('strikethrough')
22900             );
22901         };
22902         if(!this.disable.fontSize){
22903             tb.add(
22904                 '-',
22905                 
22906                 
22907                 btn('increasefontsize', false, editorcore.adjustFont),
22908                 btn('decreasefontsize', false, editorcore.adjustFont)
22909             );
22910         };
22911         
22912         
22913         if(!this.disable.colors){
22914             tb.add(
22915                 '-', {
22916                     id:editorcore.frameId +'-forecolor',
22917                     cls:'x-btn-icon x-edit-forecolor',
22918                     clickEvent:'mousedown',
22919                     tooltip: this.buttonTips['forecolor'] || undefined,
22920                     tabIndex:-1,
22921                     menu : new Roo.menu.ColorMenu({
22922                         allowReselect: true,
22923                         focus: Roo.emptyFn,
22924                         value:'000000',
22925                         plain:true,
22926                         selectHandler: function(cp, color){
22927                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
22928                             editor.deferFocus();
22929                         },
22930                         scope: editorcore,
22931                         clickEvent:'mousedown'
22932                     })
22933                 }, {
22934                     id:editorcore.frameId +'backcolor',
22935                     cls:'x-btn-icon x-edit-backcolor',
22936                     clickEvent:'mousedown',
22937                     tooltip: this.buttonTips['backcolor'] || undefined,
22938                     tabIndex:-1,
22939                     menu : new Roo.menu.ColorMenu({
22940                         focus: Roo.emptyFn,
22941                         value:'FFFFFF',
22942                         plain:true,
22943                         allowReselect: true,
22944                         selectHandler: function(cp, color){
22945                             if(Roo.isGecko){
22946                                 editorcore.execCmd('useCSS', false);
22947                                 editorcore.execCmd('hilitecolor', color);
22948                                 editorcore.execCmd('useCSS', true);
22949                                 editor.deferFocus();
22950                             }else{
22951                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
22952                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
22953                                 editor.deferFocus();
22954                             }
22955                         },
22956                         scope:editorcore,
22957                         clickEvent:'mousedown'
22958                     })
22959                 }
22960             );
22961         };
22962         // now add all the items...
22963         
22964
22965         if(!this.disable.alignments){
22966             tb.add(
22967                 '-',
22968                 btn('justifyleft'),
22969                 btn('justifycenter'),
22970                 btn('justifyright')
22971             );
22972         };
22973
22974         //if(!Roo.isSafari){
22975             if(!this.disable.links){
22976                 tb.add(
22977                     '-',
22978                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
22979                 );
22980             };
22981
22982             if(!this.disable.lists){
22983                 tb.add(
22984                     '-',
22985                     btn('insertorderedlist'),
22986                     btn('insertunorderedlist')
22987                 );
22988             }
22989             if(!this.disable.sourceEdit){
22990                 tb.add(
22991                     '-',
22992                     btn('sourceedit', true, function(btn){
22993                         this.toggleSourceEdit(btn.pressed);
22994                     })
22995                 );
22996             }
22997         //}
22998         
22999         var smenu = { };
23000         // special menu.. - needs to be tidied up..
23001         if (!this.disable.special) {
23002             smenu = {
23003                 text: "&#169;",
23004                 cls: 'x-edit-none',
23005                 
23006                 menu : {
23007                     items : []
23008                 }
23009             };
23010             for (var i =0; i < this.specialChars.length; i++) {
23011                 smenu.menu.items.push({
23012                     
23013                     html: this.specialChars[i],
23014                     handler: function(a,b) {
23015                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
23016                         //editor.insertAtCursor(a.html);
23017                         
23018                     },
23019                     tabIndex:-1
23020                 });
23021             }
23022             
23023             
23024             tb.add(smenu);
23025             
23026             
23027         }
23028         
23029         var cmenu = { };
23030         if (!this.disable.cleanStyles) {
23031             cmenu = {
23032                 cls: 'x-btn-icon x-btn-clear',
23033                 
23034                 menu : {
23035                     items : []
23036                 }
23037             };
23038             for (var i =0; i < this.cleanStyles.length; i++) {
23039                 cmenu.menu.items.push({
23040                     actiontype : this.cleanStyles[i],
23041                     html: 'Remove ' + this.cleanStyles[i],
23042                     handler: function(a,b) {
23043 //                        Roo.log(a);
23044 //                        Roo.log(b);
23045                         var c = Roo.get(editorcore.doc.body);
23046                         c.select('[style]').each(function(s) {
23047                             s.dom.style.removeProperty(a.actiontype);
23048                         });
23049                         editorcore.syncValue();
23050                     },
23051                     tabIndex:-1
23052                 });
23053             }
23054              cmenu.menu.items.push({
23055                 actiontype : 'tablewidths',
23056                 html: 'Remove Table Widths',
23057                 handler: function(a,b) {
23058                     editorcore.cleanTableWidths();
23059                     editorcore.syncValue();
23060                 },
23061                 tabIndex:-1
23062             });
23063             cmenu.menu.items.push({
23064                 actiontype : 'word',
23065                 html: 'Remove MS Word Formating',
23066                 handler: function(a,b) {
23067                     editorcore.cleanWord();
23068                     editorcore.syncValue();
23069                 },
23070                 tabIndex:-1
23071             });
23072             
23073             cmenu.menu.items.push({
23074                 actiontype : 'all',
23075                 html: 'Remove All Styles',
23076                 handler: function(a,b) {
23077                     
23078                     var c = Roo.get(editorcore.doc.body);
23079                     c.select('[style]').each(function(s) {
23080                         s.dom.removeAttribute('style');
23081                     });
23082                     editorcore.syncValue();
23083                 },
23084                 tabIndex:-1
23085             });
23086             
23087             cmenu.menu.items.push({
23088                 actiontype : 'all',
23089                 html: 'Remove All CSS Classes',
23090                 handler: function(a,b) {
23091                     
23092                     var c = Roo.get(editorcore.doc.body);
23093                     c.select('[class]').each(function(s) {
23094                         s.dom.className = '';
23095                     });
23096                     editorcore.syncValue();
23097                 },
23098                 tabIndex:-1
23099             });
23100             
23101              cmenu.menu.items.push({
23102                 actiontype : 'tidy',
23103                 html: 'Tidy HTML Source',
23104                 handler: function(a,b) {
23105                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
23106                     editorcore.syncValue();
23107                 },
23108                 tabIndex:-1
23109             });
23110             
23111             
23112             tb.add(cmenu);
23113         }
23114          
23115         if (!this.disable.specialElements) {
23116             var semenu = {
23117                 text: "Other;",
23118                 cls: 'x-edit-none',
23119                 menu : {
23120                     items : []
23121                 }
23122             };
23123             for (var i =0; i < this.specialElements.length; i++) {
23124                 semenu.menu.items.push(
23125                     Roo.apply({ 
23126                         handler: function(a,b) {
23127                             editor.insertAtCursor(this.ihtml);
23128                         }
23129                     }, this.specialElements[i])
23130                 );
23131                     
23132             }
23133             
23134             tb.add(semenu);
23135             
23136             
23137         }
23138          
23139         
23140         if (this.btns) {
23141             for(var i =0; i< this.btns.length;i++) {
23142                 var b = Roo.factory(this.btns[i],Roo.form);
23143                 b.cls =  'x-edit-none';
23144                 
23145                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
23146                     b.cls += ' x-init-enable';
23147                 }
23148                 
23149                 b.scope = editorcore;
23150                 tb.add(b);
23151             }
23152         
23153         }
23154         
23155         
23156         
23157         // disable everything...
23158         
23159         this.tb.items.each(function(item){
23160             
23161            if(
23162                 item.id != editorcore.frameId+ '-sourceedit' && 
23163                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
23164             ){
23165                 
23166                 item.disable();
23167             }
23168         });
23169         this.rendered = true;
23170         
23171         // the all the btns;
23172         editor.on('editorevent', this.updateToolbar, this);
23173         // other toolbars need to implement this..
23174         //editor.on('editmodechange', this.updateToolbar, this);
23175     },
23176     
23177     
23178     relayBtnCmd : function(btn) {
23179         this.editorcore.relayCmd(btn.cmd);
23180     },
23181     // private used internally
23182     createLink : function(){
23183         Roo.log("create link?");
23184         var url = prompt(this.createLinkText, this.defaultLinkValue);
23185         if(url && url != 'http:/'+'/'){
23186             this.editorcore.relayCmd('createlink', url);
23187         }
23188     },
23189
23190     
23191     /**
23192      * Protected method that will not generally be called directly. It triggers
23193      * a toolbar update by reading the markup state of the current selection in the editor.
23194      */
23195     updateToolbar: function(){
23196
23197         if(!this.editorcore.activated){
23198             this.editor.onFirstFocus();
23199             return;
23200         }
23201
23202         var btns = this.tb.items.map, 
23203             doc = this.editorcore.doc,
23204             frameId = this.editorcore.frameId;
23205
23206         if(!this.disable.font && !Roo.isSafari){
23207             /*
23208             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
23209             if(name != this.fontSelect.dom.value){
23210                 this.fontSelect.dom.value = name;
23211             }
23212             */
23213         }
23214         if(!this.disable.format){
23215             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
23216             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
23217             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
23218             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
23219         }
23220         if(!this.disable.alignments){
23221             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
23222             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
23223             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
23224         }
23225         if(!Roo.isSafari && !this.disable.lists){
23226             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
23227             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
23228         }
23229         
23230         var ans = this.editorcore.getAllAncestors();
23231         if (this.formatCombo) {
23232             
23233             
23234             var store = this.formatCombo.store;
23235             this.formatCombo.setValue("");
23236             for (var i =0; i < ans.length;i++) {
23237                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23238                     // select it..
23239                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23240                     break;
23241                 }
23242             }
23243         }
23244         
23245         
23246         
23247         // hides menus... - so this cant be on a menu...
23248         Roo.menu.MenuMgr.hideAll();
23249
23250         //this.editorsyncValue();
23251     },
23252    
23253     
23254     createFontOptions : function(){
23255         var buf = [], fs = this.fontFamilies, ff, lc;
23256         
23257         
23258         
23259         for(var i = 0, len = fs.length; i< len; i++){
23260             ff = fs[i];
23261             lc = ff.toLowerCase();
23262             buf.push(
23263                 '<option value="',lc,'" style="font-family:',ff,';"',
23264                     (this.defaultFont == lc ? ' selected="true">' : '>'),
23265                     ff,
23266                 '</option>'
23267             );
23268         }
23269         return buf.join('');
23270     },
23271     
23272     toggleSourceEdit : function(sourceEditMode){
23273         
23274         Roo.log("toolbar toogle");
23275         if(sourceEditMode === undefined){
23276             sourceEditMode = !this.sourceEditMode;
23277         }
23278         this.sourceEditMode = sourceEditMode === true;
23279         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
23280         // just toggle the button?
23281         if(btn.pressed !== this.sourceEditMode){
23282             btn.toggle(this.sourceEditMode);
23283             return;
23284         }
23285         
23286         if(sourceEditMode){
23287             Roo.log("disabling buttons");
23288             this.tb.items.each(function(item){
23289                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
23290                     item.disable();
23291                 }
23292             });
23293           
23294         }else{
23295             Roo.log("enabling buttons");
23296             if(this.editorcore.initialized){
23297                 this.tb.items.each(function(item){
23298                     item.enable();
23299                 });
23300             }
23301             
23302         }
23303         Roo.log("calling toggole on editor");
23304         // tell the editor that it's been pressed..
23305         this.editor.toggleSourceEdit(sourceEditMode);
23306        
23307     },
23308      /**
23309      * Object collection of toolbar tooltips for the buttons in the editor. The key
23310      * is the command id associated with that button and the value is a valid QuickTips object.
23311      * For example:
23312 <pre><code>
23313 {
23314     bold : {
23315         title: 'Bold (Ctrl+B)',
23316         text: 'Make the selected text bold.',
23317         cls: 'x-html-editor-tip'
23318     },
23319     italic : {
23320         title: 'Italic (Ctrl+I)',
23321         text: 'Make the selected text italic.',
23322         cls: 'x-html-editor-tip'
23323     },
23324     ...
23325 </code></pre>
23326     * @type Object
23327      */
23328     buttonTips : {
23329         bold : {
23330             title: 'Bold (Ctrl+B)',
23331             text: 'Make the selected text bold.',
23332             cls: 'x-html-editor-tip'
23333         },
23334         italic : {
23335             title: 'Italic (Ctrl+I)',
23336             text: 'Make the selected text italic.',
23337             cls: 'x-html-editor-tip'
23338         },
23339         underline : {
23340             title: 'Underline (Ctrl+U)',
23341             text: 'Underline the selected text.',
23342             cls: 'x-html-editor-tip'
23343         },
23344         strikethrough : {
23345             title: 'Strikethrough',
23346             text: 'Strikethrough the selected text.',
23347             cls: 'x-html-editor-tip'
23348         },
23349         increasefontsize : {
23350             title: 'Grow Text',
23351             text: 'Increase the font size.',
23352             cls: 'x-html-editor-tip'
23353         },
23354         decreasefontsize : {
23355             title: 'Shrink Text',
23356             text: 'Decrease the font size.',
23357             cls: 'x-html-editor-tip'
23358         },
23359         backcolor : {
23360             title: 'Text Highlight Color',
23361             text: 'Change the background color of the selected text.',
23362             cls: 'x-html-editor-tip'
23363         },
23364         forecolor : {
23365             title: 'Font Color',
23366             text: 'Change the color of the selected text.',
23367             cls: 'x-html-editor-tip'
23368         },
23369         justifyleft : {
23370             title: 'Align Text Left',
23371             text: 'Align text to the left.',
23372             cls: 'x-html-editor-tip'
23373         },
23374         justifycenter : {
23375             title: 'Center Text',
23376             text: 'Center text in the editor.',
23377             cls: 'x-html-editor-tip'
23378         },
23379         justifyright : {
23380             title: 'Align Text Right',
23381             text: 'Align text to the right.',
23382             cls: 'x-html-editor-tip'
23383         },
23384         insertunorderedlist : {
23385             title: 'Bullet List',
23386             text: 'Start a bulleted list.',
23387             cls: 'x-html-editor-tip'
23388         },
23389         insertorderedlist : {
23390             title: 'Numbered List',
23391             text: 'Start a numbered list.',
23392             cls: 'x-html-editor-tip'
23393         },
23394         createlink : {
23395             title: 'Hyperlink',
23396             text: 'Make the selected text a hyperlink.',
23397             cls: 'x-html-editor-tip'
23398         },
23399         sourceedit : {
23400             title: 'Source Edit',
23401             text: 'Switch to source editing mode.',
23402             cls: 'x-html-editor-tip'
23403         }
23404     },
23405     // private
23406     onDestroy : function(){
23407         if(this.rendered){
23408             
23409             this.tb.items.each(function(item){
23410                 if(item.menu){
23411                     item.menu.removeAll();
23412                     if(item.menu.el){
23413                         item.menu.el.destroy();
23414                     }
23415                 }
23416                 item.destroy();
23417             });
23418              
23419         }
23420     },
23421     onFirstFocus: function() {
23422         this.tb.items.each(function(item){
23423            item.enable();
23424         });
23425     }
23426 });
23427
23428
23429
23430
23431 // <script type="text/javascript">
23432 /*
23433  * Based on
23434  * Ext JS Library 1.1.1
23435  * Copyright(c) 2006-2007, Ext JS, LLC.
23436  *  
23437  
23438  */
23439
23440  
23441 /**
23442  * @class Roo.form.HtmlEditor.ToolbarContext
23443  * Context Toolbar
23444  * 
23445  * Usage:
23446  *
23447  new Roo.form.HtmlEditor({
23448     ....
23449     toolbars : [
23450         { xtype: 'ToolbarStandard', styles : {} }
23451         { xtype: 'ToolbarContext', disable : {} }
23452     ]
23453 })
23454
23455      
23456  * 
23457  * @config : {Object} disable List of elements to disable.. (not done yet.)
23458  * @config : {Object} styles  Map of styles available.
23459  * 
23460  */
23461
23462 Roo.form.HtmlEditor.ToolbarContext = function(config)
23463 {
23464     
23465     Roo.apply(this, config);
23466     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23467     // dont call parent... till later.
23468     this.styles = this.styles || {};
23469 }
23470
23471  
23472
23473 Roo.form.HtmlEditor.ToolbarContext.types = {
23474     'IMG' : {
23475         width : {
23476             title: "Width",
23477             width: 40
23478         },
23479         height:  {
23480             title: "Height",
23481             width: 40
23482         },
23483         align: {
23484             title: "Align",
23485             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
23486             width : 80
23487             
23488         },
23489         border: {
23490             title: "Border",
23491             width: 40
23492         },
23493         alt: {
23494             title: "Alt",
23495             width: 120
23496         },
23497         src : {
23498             title: "Src",
23499             width: 220
23500         }
23501         
23502     },
23503     'A' : {
23504         name : {
23505             title: "Name",
23506             width: 50
23507         },
23508         target:  {
23509             title: "Target",
23510             width: 120
23511         },
23512         href:  {
23513             title: "Href",
23514             width: 220
23515         } // border?
23516         
23517     },
23518     'TABLE' : {
23519         rows : {
23520             title: "Rows",
23521             width: 20
23522         },
23523         cols : {
23524             title: "Cols",
23525             width: 20
23526         },
23527         width : {
23528             title: "Width",
23529             width: 40
23530         },
23531         height : {
23532             title: "Height",
23533             width: 40
23534         },
23535         border : {
23536             title: "Border",
23537             width: 20
23538         }
23539     },
23540     'TD' : {
23541         width : {
23542             title: "Width",
23543             width: 40
23544         },
23545         height : {
23546             title: "Height",
23547             width: 40
23548         },   
23549         align: {
23550             title: "Align",
23551             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
23552             width: 80
23553         },
23554         valign: {
23555             title: "Valign",
23556             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
23557             width: 80
23558         },
23559         colspan: {
23560             title: "Colspan",
23561             width: 20
23562             
23563         },
23564          'font-family'  : {
23565             title : "Font",
23566             style : 'fontFamily',
23567             displayField: 'display',
23568             optname : 'font-family',
23569             width: 140
23570         }
23571     },
23572     'INPUT' : {
23573         name : {
23574             title: "name",
23575             width: 120
23576         },
23577         value : {
23578             title: "Value",
23579             width: 120
23580         },
23581         width : {
23582             title: "Width",
23583             width: 40
23584         }
23585     },
23586     'LABEL' : {
23587         'for' : {
23588             title: "For",
23589             width: 120
23590         }
23591     },
23592     'TEXTAREA' : {
23593           name : {
23594             title: "name",
23595             width: 120
23596         },
23597         rows : {
23598             title: "Rows",
23599             width: 20
23600         },
23601         cols : {
23602             title: "Cols",
23603             width: 20
23604         }
23605     },
23606     'SELECT' : {
23607         name : {
23608             title: "name",
23609             width: 120
23610         },
23611         selectoptions : {
23612             title: "Options",
23613             width: 200
23614         }
23615     },
23616     
23617     // should we really allow this??
23618     // should this just be 
23619     'BODY' : {
23620         title : {
23621             title: "Title",
23622             width: 200,
23623             disabled : true
23624         }
23625     },
23626     'SPAN' : {
23627         'font-family'  : {
23628             title : "Font",
23629             style : 'fontFamily',
23630             displayField: 'display',
23631             optname : 'font-family',
23632             width: 140
23633         }
23634     },
23635     'DIV' : {
23636         'font-family'  : {
23637             title : "Font",
23638             style : 'fontFamily',
23639             displayField: 'display',
23640             optname : 'font-family',
23641             width: 140
23642         }
23643     },
23644      'P' : {
23645         'font-family'  : {
23646             title : "Font",
23647             style : 'fontFamily',
23648             displayField: 'display',
23649             optname : 'font-family',
23650             width: 140
23651         }
23652     },
23653     
23654     '*' : {
23655         // empty..
23656     }
23657
23658 };
23659
23660 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
23661 Roo.form.HtmlEditor.ToolbarContext.stores = false;
23662
23663 Roo.form.HtmlEditor.ToolbarContext.options = {
23664         'font-family'  : [ 
23665                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
23666                 [ 'Courier New', 'Courier New'],
23667                 [ 'Tahoma', 'Tahoma'],
23668                 [ 'Times New Roman,serif', 'Times'],
23669                 [ 'Verdana','Verdana' ]
23670         ]
23671 };
23672
23673 // fixme - these need to be configurable..
23674  
23675
23676 //Roo.form.HtmlEditor.ToolbarContext.types
23677
23678
23679 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
23680     
23681     tb: false,
23682     
23683     rendered: false,
23684     
23685     editor : false,
23686     editorcore : false,
23687     /**
23688      * @cfg {Object} disable  List of toolbar elements to disable
23689          
23690      */
23691     disable : false,
23692     /**
23693      * @cfg {Object} styles List of styles 
23694      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
23695      *
23696      * These must be defined in the page, so they get rendered correctly..
23697      * .headline { }
23698      * TD.underline { }
23699      * 
23700      */
23701     styles : false,
23702     
23703     options: false,
23704     
23705     toolbars : false,
23706     
23707     init : function(editor)
23708     {
23709         this.editor = editor;
23710         this.editorcore = editor.editorcore ? editor.editorcore : editor;
23711         var editorcore = this.editorcore;
23712         
23713         var fid = editorcore.frameId;
23714         var etb = this;
23715         function btn(id, toggle, handler){
23716             var xid = fid + '-'+ id ;
23717             return {
23718                 id : xid,
23719                 cmd : id,
23720                 cls : 'x-btn-icon x-edit-'+id,
23721                 enableToggle:toggle !== false,
23722                 scope: editorcore, // was editor...
23723                 handler:handler||editorcore.relayBtnCmd,
23724                 clickEvent:'mousedown',
23725                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
23726                 tabIndex:-1
23727             };
23728         }
23729         // create a new element.
23730         var wdiv = editor.wrap.createChild({
23731                 tag: 'div'
23732             }, editor.wrap.dom.firstChild.nextSibling, true);
23733         
23734         // can we do this more than once??
23735         
23736          // stop form submits
23737       
23738  
23739         // disable everything...
23740         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
23741         this.toolbars = {};
23742            
23743         for (var i in  ty) {
23744           
23745             this.toolbars[i] = this.buildToolbar(ty[i],i);
23746         }
23747         this.tb = this.toolbars.BODY;
23748         this.tb.el.show();
23749         this.buildFooter();
23750         this.footer.show();
23751         editor.on('hide', function( ) { this.footer.hide() }, this);
23752         editor.on('show', function( ) { this.footer.show() }, this);
23753         
23754          
23755         this.rendered = true;
23756         
23757         // the all the btns;
23758         editor.on('editorevent', this.updateToolbar, this);
23759         // other toolbars need to implement this..
23760         //editor.on('editmodechange', this.updateToolbar, this);
23761     },
23762     
23763     
23764     
23765     /**
23766      * Protected method that will not generally be called directly. It triggers
23767      * a toolbar update by reading the markup state of the current selection in the editor.
23768      *
23769      * Note you can force an update by calling on('editorevent', scope, false)
23770      */
23771     updateToolbar: function(editor,ev,sel){
23772
23773         //Roo.log(ev);
23774         // capture mouse up - this is handy for selecting images..
23775         // perhaps should go somewhere else...
23776         if(!this.editorcore.activated){
23777              this.editor.onFirstFocus();
23778             return;
23779         }
23780         
23781         
23782         
23783         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
23784         // selectNode - might want to handle IE?
23785         if (ev &&
23786             (ev.type == 'mouseup' || ev.type == 'click' ) &&
23787             ev.target && ev.target.tagName == 'IMG') {
23788             // they have click on an image...
23789             // let's see if we can change the selection...
23790             sel = ev.target;
23791          
23792               var nodeRange = sel.ownerDocument.createRange();
23793             try {
23794                 nodeRange.selectNode(sel);
23795             } catch (e) {
23796                 nodeRange.selectNodeContents(sel);
23797             }
23798             //nodeRange.collapse(true);
23799             var s = this.editorcore.win.getSelection();
23800             s.removeAllRanges();
23801             s.addRange(nodeRange);
23802         }  
23803         
23804       
23805         var updateFooter = sel ? false : true;
23806         
23807         
23808         var ans = this.editorcore.getAllAncestors();
23809         
23810         // pick
23811         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
23812         
23813         if (!sel) { 
23814             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
23815             sel = sel ? sel : this.editorcore.doc.body;
23816             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
23817             
23818         }
23819         // pick a menu that exists..
23820         var tn = sel.tagName.toUpperCase();
23821         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
23822         
23823         tn = sel.tagName.toUpperCase();
23824         
23825         var lastSel = this.tb.selectedNode;
23826         
23827         this.tb.selectedNode = sel;
23828         
23829         // if current menu does not match..
23830         
23831         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
23832                 
23833             this.tb.el.hide();
23834             ///console.log("show: " + tn);
23835             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
23836             this.tb.el.show();
23837             // update name
23838             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
23839             
23840             
23841             // update attributes
23842             if (this.tb.fields) {
23843                 this.tb.fields.each(function(e) {
23844                     if (e.stylename) {
23845                         e.setValue(sel.style[e.stylename]);
23846                         return;
23847                     } 
23848                    e.setValue(sel.getAttribute(e.attrname));
23849                 });
23850             }
23851             
23852             var hasStyles = false;
23853             for(var i in this.styles) {
23854                 hasStyles = true;
23855                 break;
23856             }
23857             
23858             // update styles
23859             if (hasStyles) { 
23860                 var st = this.tb.fields.item(0);
23861                 
23862                 st.store.removeAll();
23863                
23864                 
23865                 var cn = sel.className.split(/\s+/);
23866                 
23867                 var avs = [];
23868                 if (this.styles['*']) {
23869                     
23870                     Roo.each(this.styles['*'], function(v) {
23871                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
23872                     });
23873                 }
23874                 if (this.styles[tn]) { 
23875                     Roo.each(this.styles[tn], function(v) {
23876                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
23877                     });
23878                 }
23879                 
23880                 st.store.loadData(avs);
23881                 st.collapse();
23882                 st.setValue(cn);
23883             }
23884             // flag our selected Node.
23885             this.tb.selectedNode = sel;
23886            
23887            
23888             Roo.menu.MenuMgr.hideAll();
23889
23890         }
23891         
23892         if (!updateFooter) {
23893             //this.footDisp.dom.innerHTML = ''; 
23894             return;
23895         }
23896         // update the footer
23897         //
23898         var html = '';
23899         
23900         this.footerEls = ans.reverse();
23901         Roo.each(this.footerEls, function(a,i) {
23902             if (!a) { return; }
23903             html += html.length ? ' &gt; '  :  '';
23904             
23905             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
23906             
23907         });
23908        
23909         // 
23910         var sz = this.footDisp.up('td').getSize();
23911         this.footDisp.dom.style.width = (sz.width -10) + 'px';
23912         this.footDisp.dom.style.marginLeft = '5px';
23913         
23914         this.footDisp.dom.style.overflow = 'hidden';
23915         
23916         this.footDisp.dom.innerHTML = html;
23917             
23918         //this.editorsyncValue();
23919     },
23920      
23921     
23922    
23923        
23924     // private
23925     onDestroy : function(){
23926         if(this.rendered){
23927             
23928             this.tb.items.each(function(item){
23929                 if(item.menu){
23930                     item.menu.removeAll();
23931                     if(item.menu.el){
23932                         item.menu.el.destroy();
23933                     }
23934                 }
23935                 item.destroy();
23936             });
23937              
23938         }
23939     },
23940     onFirstFocus: function() {
23941         // need to do this for all the toolbars..
23942         this.tb.items.each(function(item){
23943            item.enable();
23944         });
23945     },
23946     buildToolbar: function(tlist, nm)
23947     {
23948         var editor = this.editor;
23949         var editorcore = this.editorcore;
23950          // create a new element.
23951         var wdiv = editor.wrap.createChild({
23952                 tag: 'div'
23953             }, editor.wrap.dom.firstChild.nextSibling, true);
23954         
23955        
23956         var tb = new Roo.Toolbar(wdiv);
23957         // add the name..
23958         
23959         tb.add(nm+ ":&nbsp;");
23960         
23961         var styles = [];
23962         for(var i in this.styles) {
23963             styles.push(i);
23964         }
23965         
23966         // styles...
23967         if (styles && styles.length) {
23968             
23969             // this needs a multi-select checkbox...
23970             tb.addField( new Roo.form.ComboBox({
23971                 store: new Roo.data.SimpleStore({
23972                     id : 'val',
23973                     fields: ['val', 'selected'],
23974                     data : [] 
23975                 }),
23976                 name : '-roo-edit-className',
23977                 attrname : 'className',
23978                 displayField: 'val',
23979                 typeAhead: false,
23980                 mode: 'local',
23981                 editable : false,
23982                 triggerAction: 'all',
23983                 emptyText:'Select Style',
23984                 selectOnFocus:true,
23985                 width: 130,
23986                 listeners : {
23987                     'select': function(c, r, i) {
23988                         // initial support only for on class per el..
23989                         tb.selectedNode.className =  r ? r.get('val') : '';
23990                         editorcore.syncValue();
23991                     }
23992                 }
23993     
23994             }));
23995         }
23996         
23997         var tbc = Roo.form.HtmlEditor.ToolbarContext;
23998         var tbops = tbc.options;
23999         
24000         for (var i in tlist) {
24001             
24002             var item = tlist[i];
24003             tb.add(item.title + ":&nbsp;");
24004             
24005             
24006             //optname == used so you can configure the options available..
24007             var opts = item.opts ? item.opts : false;
24008             if (item.optname) {
24009                 opts = tbops[item.optname];
24010            
24011             }
24012             
24013             if (opts) {
24014                 // opts == pulldown..
24015                 tb.addField( new Roo.form.ComboBox({
24016                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
24017                         id : 'val',
24018                         fields: ['val', 'display'],
24019                         data : opts  
24020                     }),
24021                     name : '-roo-edit-' + i,
24022                     attrname : i,
24023                     stylename : item.style ? item.style : false,
24024                     displayField: item.displayField ? item.displayField : 'val',
24025                     valueField :  'val',
24026                     typeAhead: false,
24027                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
24028                     editable : false,
24029                     triggerAction: 'all',
24030                     emptyText:'Select',
24031                     selectOnFocus:true,
24032                     width: item.width ? item.width  : 130,
24033                     listeners : {
24034                         'select': function(c, r, i) {
24035                             if (c.stylename) {
24036                                 tb.selectedNode.style[c.stylename] =  r.get('val');
24037                                 return;
24038                             }
24039                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
24040                         }
24041                     }
24042
24043                 }));
24044                 continue;
24045                     
24046                  
24047                 
24048                 tb.addField( new Roo.form.TextField({
24049                     name: i,
24050                     width: 100,
24051                     //allowBlank:false,
24052                     value: ''
24053                 }));
24054                 continue;
24055             }
24056             tb.addField( new Roo.form.TextField({
24057                 name: '-roo-edit-' + i,
24058                 attrname : i,
24059                 
24060                 width: item.width,
24061                 //allowBlank:true,
24062                 value: '',
24063                 listeners: {
24064                     'change' : function(f, nv, ov) {
24065                         tb.selectedNode.setAttribute(f.attrname, nv);
24066                         editorcore.syncValue();
24067                     }
24068                 }
24069             }));
24070              
24071         }
24072         
24073         var _this = this;
24074         
24075         if(nm == 'BODY'){
24076             tb.addSeparator();
24077         
24078             tb.addButton( {
24079                 text: 'Stylesheets',
24080
24081                 listeners : {
24082                     click : function ()
24083                     {
24084                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
24085                     }
24086                 }
24087             });
24088         }
24089         
24090         tb.addFill();
24091         tb.addButton( {
24092             text: 'Remove Tag',
24093     
24094             listeners : {
24095                 click : function ()
24096                 {
24097                     // remove
24098                     // undo does not work.
24099                      
24100                     var sn = tb.selectedNode;
24101                     
24102                     var pn = sn.parentNode;
24103                     
24104                     var stn =  sn.childNodes[0];
24105                     var en = sn.childNodes[sn.childNodes.length - 1 ];
24106                     while (sn.childNodes.length) {
24107                         var node = sn.childNodes[0];
24108                         sn.removeChild(node);
24109                         //Roo.log(node);
24110                         pn.insertBefore(node, sn);
24111                         
24112                     }
24113                     pn.removeChild(sn);
24114                     var range = editorcore.createRange();
24115         
24116                     range.setStart(stn,0);
24117                     range.setEnd(en,0); //????
24118                     //range.selectNode(sel);
24119                     
24120                     
24121                     var selection = editorcore.getSelection();
24122                     selection.removeAllRanges();
24123                     selection.addRange(range);
24124                     
24125                     
24126                     
24127                     //_this.updateToolbar(null, null, pn);
24128                     _this.updateToolbar(null, null, null);
24129                     _this.footDisp.dom.innerHTML = ''; 
24130                 }
24131             }
24132             
24133                     
24134                 
24135             
24136         });
24137         
24138         
24139         tb.el.on('click', function(e){
24140             e.preventDefault(); // what does this do?
24141         });
24142         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
24143         tb.el.hide();
24144         tb.name = nm;
24145         // dont need to disable them... as they will get hidden
24146         return tb;
24147          
24148         
24149     },
24150     buildFooter : function()
24151     {
24152         
24153         var fel = this.editor.wrap.createChild();
24154         this.footer = new Roo.Toolbar(fel);
24155         // toolbar has scrolly on left / right?
24156         var footDisp= new Roo.Toolbar.Fill();
24157         var _t = this;
24158         this.footer.add(
24159             {
24160                 text : '&lt;',
24161                 xtype: 'Button',
24162                 handler : function() {
24163                     _t.footDisp.scrollTo('left',0,true)
24164                 }
24165             }
24166         );
24167         this.footer.add( footDisp );
24168         this.footer.add( 
24169             {
24170                 text : '&gt;',
24171                 xtype: 'Button',
24172                 handler : function() {
24173                     // no animation..
24174                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
24175                 }
24176             }
24177         );
24178         var fel = Roo.get(footDisp.el);
24179         fel.addClass('x-editor-context');
24180         this.footDispWrap = fel; 
24181         this.footDispWrap.overflow  = 'hidden';
24182         
24183         this.footDisp = fel.createChild();
24184         this.footDispWrap.on('click', this.onContextClick, this)
24185         
24186         
24187     },
24188     onContextClick : function (ev,dom)
24189     {
24190         ev.preventDefault();
24191         var  cn = dom.className;
24192         //Roo.log(cn);
24193         if (!cn.match(/x-ed-loc-/)) {
24194             return;
24195         }
24196         var n = cn.split('-').pop();
24197         var ans = this.footerEls;
24198         var sel = ans[n];
24199         
24200          // pick
24201         var range = this.editorcore.createRange();
24202         
24203         range.selectNodeContents(sel);
24204         //range.selectNode(sel);
24205         
24206         
24207         var selection = this.editorcore.getSelection();
24208         selection.removeAllRanges();
24209         selection.addRange(range);
24210         
24211         
24212         
24213         this.updateToolbar(null, null, sel);
24214         
24215         
24216     }
24217     
24218     
24219     
24220     
24221     
24222 });
24223
24224
24225
24226
24227
24228 /*
24229  * Based on:
24230  * Ext JS Library 1.1.1
24231  * Copyright(c) 2006-2007, Ext JS, LLC.
24232  *
24233  * Originally Released Under LGPL - original licence link has changed is not relivant.
24234  *
24235  * Fork - LGPL
24236  * <script type="text/javascript">
24237  */
24238  
24239 /**
24240  * @class Roo.form.BasicForm
24241  * @extends Roo.util.Observable
24242  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
24243  * @constructor
24244  * @param {String/HTMLElement/Roo.Element} el The form element or its id
24245  * @param {Object} config Configuration options
24246  */
24247 Roo.form.BasicForm = function(el, config){
24248     this.allItems = [];
24249     this.childForms = [];
24250     Roo.apply(this, config);
24251     /*
24252      * The Roo.form.Field items in this form.
24253      * @type MixedCollection
24254      */
24255      
24256      
24257     this.items = new Roo.util.MixedCollection(false, function(o){
24258         return o.id || (o.id = Roo.id());
24259     });
24260     this.addEvents({
24261         /**
24262          * @event beforeaction
24263          * Fires before any action is performed. Return false to cancel the action.
24264          * @param {Form} this
24265          * @param {Action} action The action to be performed
24266          */
24267         beforeaction: true,
24268         /**
24269          * @event actionfailed
24270          * Fires when an action fails.
24271          * @param {Form} this
24272          * @param {Action} action The action that failed
24273          */
24274         actionfailed : true,
24275         /**
24276          * @event actioncomplete
24277          * Fires when an action is completed.
24278          * @param {Form} this
24279          * @param {Action} action The action that completed
24280          */
24281         actioncomplete : true
24282     });
24283     if(el){
24284         this.initEl(el);
24285     }
24286     Roo.form.BasicForm.superclass.constructor.call(this);
24287 };
24288
24289 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
24290     /**
24291      * @cfg {String} method
24292      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
24293      */
24294     /**
24295      * @cfg {DataReader} reader
24296      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
24297      * This is optional as there is built-in support for processing JSON.
24298      */
24299     /**
24300      * @cfg {DataReader} errorReader
24301      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
24302      * This is completely optional as there is built-in support for processing JSON.
24303      */
24304     /**
24305      * @cfg {String} url
24306      * The URL to use for form actions if one isn't supplied in the action options.
24307      */
24308     /**
24309      * @cfg {Boolean} fileUpload
24310      * Set to true if this form is a file upload.
24311      */
24312      
24313     /**
24314      * @cfg {Object} baseParams
24315      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
24316      */
24317      /**
24318      
24319     /**
24320      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
24321      */
24322     timeout: 30,
24323
24324     // private
24325     activeAction : null,
24326
24327     /**
24328      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
24329      * or setValues() data instead of when the form was first created.
24330      */
24331     trackResetOnLoad : false,
24332     
24333     
24334     /**
24335      * childForms - used for multi-tab forms
24336      * @type {Array}
24337      */
24338     childForms : false,
24339     
24340     /**
24341      * allItems - full list of fields.
24342      * @type {Array}
24343      */
24344     allItems : false,
24345     
24346     /**
24347      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
24348      * element by passing it or its id or mask the form itself by passing in true.
24349      * @type Mixed
24350      */
24351     waitMsgTarget : false,
24352
24353     // private
24354     initEl : function(el){
24355         this.el = Roo.get(el);
24356         this.id = this.el.id || Roo.id();
24357         this.el.on('submit', this.onSubmit, this);
24358         this.el.addClass('x-form');
24359     },
24360
24361     // private
24362     onSubmit : function(e){
24363         e.stopEvent();
24364     },
24365
24366     /**
24367      * Returns true if client-side validation on the form is successful.
24368      * @return Boolean
24369      */
24370     isValid : function(){
24371         var valid = true;
24372         this.items.each(function(f){
24373            if(!f.validate()){
24374                valid = false;
24375            }
24376         });
24377         return valid;
24378     },
24379
24380     /**
24381      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
24382      * @return Boolean
24383      */
24384     isDirty : function(){
24385         var dirty = false;
24386         this.items.each(function(f){
24387            if(f.isDirty()){
24388                dirty = true;
24389                return false;
24390            }
24391         });
24392         return dirty;
24393     },
24394     
24395     /**
24396      * Returns true if any fields in this form have changed since their original load. (New version)
24397      * @return Boolean
24398      */
24399     
24400     hasChanged : function()
24401     {
24402         var dirty = false;
24403         this.items.each(function(f){
24404            if(f.hasChanged()){
24405                dirty = true;
24406                return false;
24407            }
24408         });
24409         return dirty;
24410         
24411     },
24412     /**
24413      * Resets all hasChanged to 'false' -
24414      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
24415      * So hasChanged storage is only to be used for this purpose
24416      * @return Boolean
24417      */
24418     resetHasChanged : function()
24419     {
24420         this.items.each(function(f){
24421            f.resetHasChanged();
24422         });
24423         
24424     },
24425     
24426     
24427     /**
24428      * Performs a predefined action (submit or load) or custom actions you define on this form.
24429      * @param {String} actionName The name of the action type
24430      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
24431      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
24432      * accept other config options):
24433      * <pre>
24434 Property          Type             Description
24435 ----------------  ---------------  ----------------------------------------------------------------------------------
24436 url               String           The url for the action (defaults to the form's url)
24437 method            String           The form method to use (defaults to the form's method, or POST if not defined)
24438 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
24439 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
24440                                    validate the form on the client (defaults to false)
24441      * </pre>
24442      * @return {BasicForm} this
24443      */
24444     doAction : function(action, options){
24445         if(typeof action == 'string'){
24446             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
24447         }
24448         if(this.fireEvent('beforeaction', this, action) !== false){
24449             this.beforeAction(action);
24450             action.run.defer(100, action);
24451         }
24452         return this;
24453     },
24454
24455     /**
24456      * Shortcut to do a submit action.
24457      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
24458      * @return {BasicForm} this
24459      */
24460     submit : function(options){
24461         this.doAction('submit', options);
24462         return this;
24463     },
24464
24465     /**
24466      * Shortcut to do a load action.
24467      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
24468      * @return {BasicForm} this
24469      */
24470     load : function(options){
24471         this.doAction('load', options);
24472         return this;
24473     },
24474
24475     /**
24476      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
24477      * @param {Record} record The record to edit
24478      * @return {BasicForm} this
24479      */
24480     updateRecord : function(record){
24481         record.beginEdit();
24482         var fs = record.fields;
24483         fs.each(function(f){
24484             var field = this.findField(f.name);
24485             if(field){
24486                 record.set(f.name, field.getValue());
24487             }
24488         }, this);
24489         record.endEdit();
24490         return this;
24491     },
24492
24493     /**
24494      * Loads an Roo.data.Record into this form.
24495      * @param {Record} record The record to load
24496      * @return {BasicForm} this
24497      */
24498     loadRecord : function(record){
24499         this.setValues(record.data);
24500         return this;
24501     },
24502
24503     // private
24504     beforeAction : function(action){
24505         var o = action.options;
24506         
24507        
24508         if(this.waitMsgTarget === true){
24509             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
24510         }else if(this.waitMsgTarget){
24511             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
24512             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
24513         }else {
24514             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
24515         }
24516          
24517     },
24518
24519     // private
24520     afterAction : function(action, success){
24521         this.activeAction = null;
24522         var o = action.options;
24523         
24524         if(this.waitMsgTarget === true){
24525             this.el.unmask();
24526         }else if(this.waitMsgTarget){
24527             this.waitMsgTarget.unmask();
24528         }else{
24529             Roo.MessageBox.updateProgress(1);
24530             Roo.MessageBox.hide();
24531         }
24532          
24533         if(success){
24534             if(o.reset){
24535                 this.reset();
24536             }
24537             Roo.callback(o.success, o.scope, [this, action]);
24538             this.fireEvent('actioncomplete', this, action);
24539             
24540         }else{
24541             
24542             // failure condition..
24543             // we have a scenario where updates need confirming.
24544             // eg. if a locking scenario exists..
24545             // we look for { errors : { needs_confirm : true }} in the response.
24546             if (
24547                 (typeof(action.result) != 'undefined')  &&
24548                 (typeof(action.result.errors) != 'undefined')  &&
24549                 (typeof(action.result.errors.needs_confirm) != 'undefined')
24550            ){
24551                 var _t = this;
24552                 Roo.MessageBox.confirm(
24553                     "Change requires confirmation",
24554                     action.result.errorMsg,
24555                     function(r) {
24556                         if (r != 'yes') {
24557                             return;
24558                         }
24559                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
24560                     }
24561                     
24562                 );
24563                 
24564                 
24565                 
24566                 return;
24567             }
24568             
24569             Roo.callback(o.failure, o.scope, [this, action]);
24570             // show an error message if no failed handler is set..
24571             if (!this.hasListener('actionfailed')) {
24572                 Roo.MessageBox.alert("Error",
24573                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
24574                         action.result.errorMsg :
24575                         "Saving Failed, please check your entries or try again"
24576                 );
24577             }
24578             
24579             this.fireEvent('actionfailed', this, action);
24580         }
24581         
24582     },
24583
24584     /**
24585      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
24586      * @param {String} id The value to search for
24587      * @return Field
24588      */
24589     findField : function(id){
24590         var field = this.items.get(id);
24591         if(!field){
24592             this.items.each(function(f){
24593                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
24594                     field = f;
24595                     return false;
24596                 }
24597             });
24598         }
24599         return field || null;
24600     },
24601
24602     /**
24603      * Add a secondary form to this one, 
24604      * Used to provide tabbed forms. One form is primary, with hidden values 
24605      * which mirror the elements from the other forms.
24606      * 
24607      * @param {Roo.form.Form} form to add.
24608      * 
24609      */
24610     addForm : function(form)
24611     {
24612        
24613         if (this.childForms.indexOf(form) > -1) {
24614             // already added..
24615             return;
24616         }
24617         this.childForms.push(form);
24618         var n = '';
24619         Roo.each(form.allItems, function (fe) {
24620             
24621             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
24622             if (this.findField(n)) { // already added..
24623                 return;
24624             }
24625             var add = new Roo.form.Hidden({
24626                 name : n
24627             });
24628             add.render(this.el);
24629             
24630             this.add( add );
24631         }, this);
24632         
24633     },
24634     /**
24635      * Mark fields in this form invalid in bulk.
24636      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
24637      * @return {BasicForm} this
24638      */
24639     markInvalid : function(errors){
24640         if(errors instanceof Array){
24641             for(var i = 0, len = errors.length; i < len; i++){
24642                 var fieldError = errors[i];
24643                 var f = this.findField(fieldError.id);
24644                 if(f){
24645                     f.markInvalid(fieldError.msg);
24646                 }
24647             }
24648         }else{
24649             var field, id;
24650             for(id in errors){
24651                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
24652                     field.markInvalid(errors[id]);
24653                 }
24654             }
24655         }
24656         Roo.each(this.childForms || [], function (f) {
24657             f.markInvalid(errors);
24658         });
24659         
24660         return this;
24661     },
24662
24663     /**
24664      * Set values for fields in this form in bulk.
24665      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
24666      * @return {BasicForm} this
24667      */
24668     setValues : function(values){
24669         if(values instanceof Array){ // array of objects
24670             for(var i = 0, len = values.length; i < len; i++){
24671                 var v = values[i];
24672                 var f = this.findField(v.id);
24673                 if(f){
24674                     f.setValue(v.value);
24675                     if(this.trackResetOnLoad){
24676                         f.originalValue = f.getValue();
24677                     }
24678                 }
24679             }
24680         }else{ // object hash
24681             var field, id;
24682             for(id in values){
24683                 if(typeof values[id] != 'function' && (field = this.findField(id))){
24684                     
24685                     if (field.setFromData && 
24686                         field.valueField && 
24687                         field.displayField &&
24688                         // combos' with local stores can 
24689                         // be queried via setValue()
24690                         // to set their value..
24691                         (field.store && !field.store.isLocal)
24692                         ) {
24693                         // it's a combo
24694                         var sd = { };
24695                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
24696                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
24697                         field.setFromData(sd);
24698                         
24699                     } else {
24700                         field.setValue(values[id]);
24701                     }
24702                     
24703                     
24704                     if(this.trackResetOnLoad){
24705                         field.originalValue = field.getValue();
24706                     }
24707                 }
24708             }
24709         }
24710         this.resetHasChanged();
24711         
24712         
24713         Roo.each(this.childForms || [], function (f) {
24714             f.setValues(values);
24715             f.resetHasChanged();
24716         });
24717                 
24718         return this;
24719     },
24720
24721     /**
24722      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
24723      * they are returned as an array.
24724      * @param {Boolean} asString
24725      * @return {Object}
24726      */
24727     getValues : function(asString){
24728         if (this.childForms) {
24729             // copy values from the child forms
24730             Roo.each(this.childForms, function (f) {
24731                 this.setValues(f.getValues());
24732             }, this);
24733         }
24734         
24735         
24736         
24737         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
24738         if(asString === true){
24739             return fs;
24740         }
24741         return Roo.urlDecode(fs);
24742     },
24743     
24744     /**
24745      * Returns the fields in this form as an object with key/value pairs. 
24746      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
24747      * @return {Object}
24748      */
24749     getFieldValues : function(with_hidden)
24750     {
24751         if (this.childForms) {
24752             // copy values from the child forms
24753             // should this call getFieldValues - probably not as we do not currently copy
24754             // hidden fields when we generate..
24755             Roo.each(this.childForms, function (f) {
24756                 this.setValues(f.getValues());
24757             }, this);
24758         }
24759         
24760         var ret = {};
24761         this.items.each(function(f){
24762             if (!f.getName()) {
24763                 return;
24764             }
24765             var v = f.getValue();
24766             if (f.inputType =='radio') {
24767                 if (typeof(ret[f.getName()]) == 'undefined') {
24768                     ret[f.getName()] = ''; // empty..
24769                 }
24770                 
24771                 if (!f.el.dom.checked) {
24772                     return;
24773                     
24774                 }
24775                 v = f.el.dom.value;
24776                 
24777             }
24778             
24779             // not sure if this supported any more..
24780             if ((typeof(v) == 'object') && f.getRawValue) {
24781                 v = f.getRawValue() ; // dates..
24782             }
24783             // combo boxes where name != hiddenName...
24784             if (f.name != f.getName()) {
24785                 ret[f.name] = f.getRawValue();
24786             }
24787             ret[f.getName()] = v;
24788         });
24789         
24790         return ret;
24791     },
24792
24793     /**
24794      * Clears all invalid messages in this form.
24795      * @return {BasicForm} this
24796      */
24797     clearInvalid : function(){
24798         this.items.each(function(f){
24799            f.clearInvalid();
24800         });
24801         
24802         Roo.each(this.childForms || [], function (f) {
24803             f.clearInvalid();
24804         });
24805         
24806         
24807         return this;
24808     },
24809
24810     /**
24811      * Resets this form.
24812      * @return {BasicForm} this
24813      */
24814     reset : function(){
24815         this.items.each(function(f){
24816             f.reset();
24817         });
24818         
24819         Roo.each(this.childForms || [], function (f) {
24820             f.reset();
24821         });
24822         this.resetHasChanged();
24823         
24824         return this;
24825     },
24826
24827     /**
24828      * Add Roo.form components to this form.
24829      * @param {Field} field1
24830      * @param {Field} field2 (optional)
24831      * @param {Field} etc (optional)
24832      * @return {BasicForm} this
24833      */
24834     add : function(){
24835         this.items.addAll(Array.prototype.slice.call(arguments, 0));
24836         return this;
24837     },
24838
24839
24840     /**
24841      * Removes a field from the items collection (does NOT remove its markup).
24842      * @param {Field} field
24843      * @return {BasicForm} this
24844      */
24845     remove : function(field){
24846         this.items.remove(field);
24847         return this;
24848     },
24849
24850     /**
24851      * Looks at the fields in this form, checks them for an id attribute,
24852      * and calls applyTo on the existing dom element with that id.
24853      * @return {BasicForm} this
24854      */
24855     render : function(){
24856         this.items.each(function(f){
24857             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
24858                 f.applyTo(f.id);
24859             }
24860         });
24861         return this;
24862     },
24863
24864     /**
24865      * Calls {@link Ext#apply} for all fields in this form with the passed object.
24866      * @param {Object} values
24867      * @return {BasicForm} this
24868      */
24869     applyToFields : function(o){
24870         this.items.each(function(f){
24871            Roo.apply(f, o);
24872         });
24873         return this;
24874     },
24875
24876     /**
24877      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
24878      * @param {Object} values
24879      * @return {BasicForm} this
24880      */
24881     applyIfToFields : function(o){
24882         this.items.each(function(f){
24883            Roo.applyIf(f, o);
24884         });
24885         return this;
24886     }
24887 });
24888
24889 // back compat
24890 Roo.BasicForm = Roo.form.BasicForm;/*
24891  * Based on:
24892  * Ext JS Library 1.1.1
24893  * Copyright(c) 2006-2007, Ext JS, LLC.
24894  *
24895  * Originally Released Under LGPL - original licence link has changed is not relivant.
24896  *
24897  * Fork - LGPL
24898  * <script type="text/javascript">
24899  */
24900
24901 /**
24902  * @class Roo.form.Form
24903  * @extends Roo.form.BasicForm
24904  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
24905  * @constructor
24906  * @param {Object} config Configuration options
24907  */
24908 Roo.form.Form = function(config){
24909     var xitems =  [];
24910     if (config.items) {
24911         xitems = config.items;
24912         delete config.items;
24913     }
24914    
24915     
24916     Roo.form.Form.superclass.constructor.call(this, null, config);
24917     this.url = this.url || this.action;
24918     if(!this.root){
24919         this.root = new Roo.form.Layout(Roo.applyIf({
24920             id: Roo.id()
24921         }, config));
24922     }
24923     this.active = this.root;
24924     /**
24925      * Array of all the buttons that have been added to this form via {@link addButton}
24926      * @type Array
24927      */
24928     this.buttons = [];
24929     this.allItems = [];
24930     this.addEvents({
24931         /**
24932          * @event clientvalidation
24933          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
24934          * @param {Form} this
24935          * @param {Boolean} valid true if the form has passed client-side validation
24936          */
24937         clientvalidation: true,
24938         /**
24939          * @event rendered
24940          * Fires when the form is rendered
24941          * @param {Roo.form.Form} form
24942          */
24943         rendered : true
24944     });
24945     
24946     if (this.progressUrl) {
24947             // push a hidden field onto the list of fields..
24948             this.addxtype( {
24949                     xns: Roo.form, 
24950                     xtype : 'Hidden', 
24951                     name : 'UPLOAD_IDENTIFIER' 
24952             });
24953         }
24954         
24955     
24956     Roo.each(xitems, this.addxtype, this);
24957     
24958     
24959     
24960 };
24961
24962 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
24963     /**
24964      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
24965      */
24966     /**
24967      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
24968      */
24969     /**
24970      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
24971      */
24972     buttonAlign:'center',
24973
24974     /**
24975      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
24976      */
24977     minButtonWidth:75,
24978
24979     /**
24980      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
24981      * This property cascades to child containers if not set.
24982      */
24983     labelAlign:'left',
24984
24985     /**
24986      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
24987      * fires a looping event with that state. This is required to bind buttons to the valid
24988      * state using the config value formBind:true on the button.
24989      */
24990     monitorValid : false,
24991
24992     /**
24993      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
24994      */
24995     monitorPoll : 200,
24996     
24997     /**
24998      * @cfg {String} progressUrl - Url to return progress data 
24999      */
25000     
25001     progressUrl : false,
25002   
25003     /**
25004      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
25005      * fields are added and the column is closed. If no fields are passed the column remains open
25006      * until end() is called.
25007      * @param {Object} config The config to pass to the column
25008      * @param {Field} field1 (optional)
25009      * @param {Field} field2 (optional)
25010      * @param {Field} etc (optional)
25011      * @return Column The column container object
25012      */
25013     column : function(c){
25014         var col = new Roo.form.Column(c);
25015         this.start(col);
25016         if(arguments.length > 1){ // duplicate code required because of Opera
25017             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25018             this.end();
25019         }
25020         return col;
25021     },
25022
25023     /**
25024      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
25025      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
25026      * until end() is called.
25027      * @param {Object} config The config to pass to the fieldset
25028      * @param {Field} field1 (optional)
25029      * @param {Field} field2 (optional)
25030      * @param {Field} etc (optional)
25031      * @return FieldSet The fieldset container object
25032      */
25033     fieldset : function(c){
25034         var fs = new Roo.form.FieldSet(c);
25035         this.start(fs);
25036         if(arguments.length > 1){ // duplicate code required because of Opera
25037             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25038             this.end();
25039         }
25040         return fs;
25041     },
25042
25043     /**
25044      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
25045      * fields are added and the container is closed. If no fields are passed the container remains open
25046      * until end() is called.
25047      * @param {Object} config The config to pass to the Layout
25048      * @param {Field} field1 (optional)
25049      * @param {Field} field2 (optional)
25050      * @param {Field} etc (optional)
25051      * @return Layout The container object
25052      */
25053     container : function(c){
25054         var l = new Roo.form.Layout(c);
25055         this.start(l);
25056         if(arguments.length > 1){ // duplicate code required because of Opera
25057             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25058             this.end();
25059         }
25060         return l;
25061     },
25062
25063     /**
25064      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
25065      * @param {Object} container A Roo.form.Layout or subclass of Layout
25066      * @return {Form} this
25067      */
25068     start : function(c){
25069         // cascade label info
25070         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
25071         this.active.stack.push(c);
25072         c.ownerCt = this.active;
25073         this.active = c;
25074         return this;
25075     },
25076
25077     /**
25078      * Closes the current open container
25079      * @return {Form} this
25080      */
25081     end : function(){
25082         if(this.active == this.root){
25083             return this;
25084         }
25085         this.active = this.active.ownerCt;
25086         return this;
25087     },
25088
25089     /**
25090      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
25091      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
25092      * as the label of the field.
25093      * @param {Field} field1
25094      * @param {Field} field2 (optional)
25095      * @param {Field} etc. (optional)
25096      * @return {Form} this
25097      */
25098     add : function(){
25099         this.active.stack.push.apply(this.active.stack, arguments);
25100         this.allItems.push.apply(this.allItems,arguments);
25101         var r = [];
25102         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
25103             if(a[i].isFormField){
25104                 r.push(a[i]);
25105             }
25106         }
25107         if(r.length > 0){
25108             Roo.form.Form.superclass.add.apply(this, r);
25109         }
25110         return this;
25111     },
25112     
25113
25114     
25115     
25116     
25117      /**
25118      * Find any element that has been added to a form, using it's ID or name
25119      * This can include framesets, columns etc. along with regular fields..
25120      * @param {String} id - id or name to find.
25121      
25122      * @return {Element} e - or false if nothing found.
25123      */
25124     findbyId : function(id)
25125     {
25126         var ret = false;
25127         if (!id) {
25128             return ret;
25129         }
25130         Roo.each(this.allItems, function(f){
25131             if (f.id == id || f.name == id ){
25132                 ret = f;
25133                 return false;
25134             }
25135         });
25136         return ret;
25137     },
25138
25139     
25140     
25141     /**
25142      * Render this form into the passed container. This should only be called once!
25143      * @param {String/HTMLElement/Element} container The element this component should be rendered into
25144      * @return {Form} this
25145      */
25146     render : function(ct)
25147     {
25148         
25149         
25150         
25151         ct = Roo.get(ct);
25152         var o = this.autoCreate || {
25153             tag: 'form',
25154             method : this.method || 'POST',
25155             id : this.id || Roo.id()
25156         };
25157         this.initEl(ct.createChild(o));
25158
25159         this.root.render(this.el);
25160         
25161        
25162              
25163         this.items.each(function(f){
25164             f.render('x-form-el-'+f.id);
25165         });
25166
25167         if(this.buttons.length > 0){
25168             // tables are required to maintain order and for correct IE layout
25169             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
25170                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
25171                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
25172             }}, null, true);
25173             var tr = tb.getElementsByTagName('tr')[0];
25174             for(var i = 0, len = this.buttons.length; i < len; i++) {
25175                 var b = this.buttons[i];
25176                 var td = document.createElement('td');
25177                 td.className = 'x-form-btn-td';
25178                 b.render(tr.appendChild(td));
25179             }
25180         }
25181         if(this.monitorValid){ // initialize after render
25182             this.startMonitoring();
25183         }
25184         this.fireEvent('rendered', this);
25185         return this;
25186     },
25187
25188     /**
25189      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
25190      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
25191      * object or a valid Roo.DomHelper element config
25192      * @param {Function} handler The function called when the button is clicked
25193      * @param {Object} scope (optional) The scope of the handler function
25194      * @return {Roo.Button}
25195      */
25196     addButton : function(config, handler, scope){
25197         var bc = {
25198             handler: handler,
25199             scope: scope,
25200             minWidth: this.minButtonWidth,
25201             hideParent:true
25202         };
25203         if(typeof config == "string"){
25204             bc.text = config;
25205         }else{
25206             Roo.apply(bc, config);
25207         }
25208         var btn = new Roo.Button(null, bc);
25209         this.buttons.push(btn);
25210         return btn;
25211     },
25212
25213      /**
25214      * Adds a series of form elements (using the xtype property as the factory method.
25215      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
25216      * @param {Object} config 
25217      */
25218     
25219     addxtype : function()
25220     {
25221         var ar = Array.prototype.slice.call(arguments, 0);
25222         var ret = false;
25223         for(var i = 0; i < ar.length; i++) {
25224             if (!ar[i]) {
25225                 continue; // skip -- if this happends something invalid got sent, we 
25226                 // should ignore it, as basically that interface element will not show up
25227                 // and that should be pretty obvious!!
25228             }
25229             
25230             if (Roo.form[ar[i].xtype]) {
25231                 ar[i].form = this;
25232                 var fe = Roo.factory(ar[i], Roo.form);
25233                 if (!ret) {
25234                     ret = fe;
25235                 }
25236                 fe.form = this;
25237                 if (fe.store) {
25238                     fe.store.form = this;
25239                 }
25240                 if (fe.isLayout) {  
25241                          
25242                     this.start(fe);
25243                     this.allItems.push(fe);
25244                     if (fe.items && fe.addxtype) {
25245                         fe.addxtype.apply(fe, fe.items);
25246                         delete fe.items;
25247                     }
25248                      this.end();
25249                     continue;
25250                 }
25251                 
25252                 
25253                  
25254                 this.add(fe);
25255               //  console.log('adding ' + ar[i].xtype);
25256             }
25257             if (ar[i].xtype == 'Button') {  
25258                 //console.log('adding button');
25259                 //console.log(ar[i]);
25260                 this.addButton(ar[i]);
25261                 this.allItems.push(fe);
25262                 continue;
25263             }
25264             
25265             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
25266                 alert('end is not supported on xtype any more, use items');
25267             //    this.end();
25268             //    //console.log('adding end');
25269             }
25270             
25271         }
25272         return ret;
25273     },
25274     
25275     /**
25276      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
25277      * option "monitorValid"
25278      */
25279     startMonitoring : function(){
25280         if(!this.bound){
25281             this.bound = true;
25282             Roo.TaskMgr.start({
25283                 run : this.bindHandler,
25284                 interval : this.monitorPoll || 200,
25285                 scope: this
25286             });
25287         }
25288     },
25289
25290     /**
25291      * Stops monitoring of the valid state of this form
25292      */
25293     stopMonitoring : function(){
25294         this.bound = false;
25295     },
25296
25297     // private
25298     bindHandler : function(){
25299         if(!this.bound){
25300             return false; // stops binding
25301         }
25302         var valid = true;
25303         this.items.each(function(f){
25304             if(!f.isValid(true)){
25305                 valid = false;
25306                 return false;
25307             }
25308         });
25309         for(var i = 0, len = this.buttons.length; i < len; i++){
25310             var btn = this.buttons[i];
25311             if(btn.formBind === true && btn.disabled === valid){
25312                 btn.setDisabled(!valid);
25313             }
25314         }
25315         this.fireEvent('clientvalidation', this, valid);
25316     }
25317     
25318     
25319     
25320     
25321     
25322     
25323     
25324     
25325 });
25326
25327
25328 // back compat
25329 Roo.Form = Roo.form.Form;
25330 /*
25331  * Based on:
25332  * Ext JS Library 1.1.1
25333  * Copyright(c) 2006-2007, Ext JS, LLC.
25334  *
25335  * Originally Released Under LGPL - original licence link has changed is not relivant.
25336  *
25337  * Fork - LGPL
25338  * <script type="text/javascript">
25339  */
25340
25341 // as we use this in bootstrap.
25342 Roo.namespace('Roo.form');
25343  /**
25344  * @class Roo.form.Action
25345  * Internal Class used to handle form actions
25346  * @constructor
25347  * @param {Roo.form.BasicForm} el The form element or its id
25348  * @param {Object} config Configuration options
25349  */
25350
25351  
25352  
25353 // define the action interface
25354 Roo.form.Action = function(form, options){
25355     this.form = form;
25356     this.options = options || {};
25357 };
25358 /**
25359  * Client Validation Failed
25360  * @const 
25361  */
25362 Roo.form.Action.CLIENT_INVALID = 'client';
25363 /**
25364  * Server Validation Failed
25365  * @const 
25366  */
25367 Roo.form.Action.SERVER_INVALID = 'server';
25368  /**
25369  * Connect to Server Failed
25370  * @const 
25371  */
25372 Roo.form.Action.CONNECT_FAILURE = 'connect';
25373 /**
25374  * Reading Data from Server Failed
25375  * @const 
25376  */
25377 Roo.form.Action.LOAD_FAILURE = 'load';
25378
25379 Roo.form.Action.prototype = {
25380     type : 'default',
25381     failureType : undefined,
25382     response : undefined,
25383     result : undefined,
25384
25385     // interface method
25386     run : function(options){
25387
25388     },
25389
25390     // interface method
25391     success : function(response){
25392
25393     },
25394
25395     // interface method
25396     handleResponse : function(response){
25397
25398     },
25399
25400     // default connection failure
25401     failure : function(response){
25402         
25403         this.response = response;
25404         this.failureType = Roo.form.Action.CONNECT_FAILURE;
25405         this.form.afterAction(this, false);
25406     },
25407
25408     processResponse : function(response){
25409         this.response = response;
25410         if(!response.responseText){
25411             return true;
25412         }
25413         this.result = this.handleResponse(response);
25414         return this.result;
25415     },
25416
25417     // utility functions used internally
25418     getUrl : function(appendParams){
25419         var url = this.options.url || this.form.url || this.form.el.dom.action;
25420         if(appendParams){
25421             var p = this.getParams();
25422             if(p){
25423                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
25424             }
25425         }
25426         return url;
25427     },
25428
25429     getMethod : function(){
25430         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
25431     },
25432
25433     getParams : function(){
25434         var bp = this.form.baseParams;
25435         var p = this.options.params;
25436         if(p){
25437             if(typeof p == "object"){
25438                 p = Roo.urlEncode(Roo.applyIf(p, bp));
25439             }else if(typeof p == 'string' && bp){
25440                 p += '&' + Roo.urlEncode(bp);
25441             }
25442         }else if(bp){
25443             p = Roo.urlEncode(bp);
25444         }
25445         return p;
25446     },
25447
25448     createCallback : function(){
25449         return {
25450             success: this.success,
25451             failure: this.failure,
25452             scope: this,
25453             timeout: (this.form.timeout*1000),
25454             upload: this.form.fileUpload ? this.success : undefined
25455         };
25456     }
25457 };
25458
25459 Roo.form.Action.Submit = function(form, options){
25460     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
25461 };
25462
25463 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
25464     type : 'submit',
25465
25466     haveProgress : false,
25467     uploadComplete : false,
25468     
25469     // uploadProgress indicator.
25470     uploadProgress : function()
25471     {
25472         if (!this.form.progressUrl) {
25473             return;
25474         }
25475         
25476         if (!this.haveProgress) {
25477             Roo.MessageBox.progress("Uploading", "Uploading");
25478         }
25479         if (this.uploadComplete) {
25480            Roo.MessageBox.hide();
25481            return;
25482         }
25483         
25484         this.haveProgress = true;
25485    
25486         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
25487         
25488         var c = new Roo.data.Connection();
25489         c.request({
25490             url : this.form.progressUrl,
25491             params: {
25492                 id : uid
25493             },
25494             method: 'GET',
25495             success : function(req){
25496                //console.log(data);
25497                 var rdata = false;
25498                 var edata;
25499                 try  {
25500                    rdata = Roo.decode(req.responseText)
25501                 } catch (e) {
25502                     Roo.log("Invalid data from server..");
25503                     Roo.log(edata);
25504                     return;
25505                 }
25506                 if (!rdata || !rdata.success) {
25507                     Roo.log(rdata);
25508                     Roo.MessageBox.alert(Roo.encode(rdata));
25509                     return;
25510                 }
25511                 var data = rdata.data;
25512                 
25513                 if (this.uploadComplete) {
25514                    Roo.MessageBox.hide();
25515                    return;
25516                 }
25517                    
25518                 if (data){
25519                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
25520                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
25521                     );
25522                 }
25523                 this.uploadProgress.defer(2000,this);
25524             },
25525        
25526             failure: function(data) {
25527                 Roo.log('progress url failed ');
25528                 Roo.log(data);
25529             },
25530             scope : this
25531         });
25532            
25533     },
25534     
25535     
25536     run : function()
25537     {
25538         // run get Values on the form, so it syncs any secondary forms.
25539         this.form.getValues();
25540         
25541         var o = this.options;
25542         var method = this.getMethod();
25543         var isPost = method == 'POST';
25544         if(o.clientValidation === false || this.form.isValid()){
25545             
25546             if (this.form.progressUrl) {
25547                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
25548                     (new Date() * 1) + '' + Math.random());
25549                     
25550             } 
25551             
25552             
25553             Roo.Ajax.request(Roo.apply(this.createCallback(), {
25554                 form:this.form.el.dom,
25555                 url:this.getUrl(!isPost),
25556                 method: method,
25557                 params:isPost ? this.getParams() : null,
25558                 isUpload: this.form.fileUpload
25559             }));
25560             
25561             this.uploadProgress();
25562
25563         }else if (o.clientValidation !== false){ // client validation failed
25564             this.failureType = Roo.form.Action.CLIENT_INVALID;
25565             this.form.afterAction(this, false);
25566         }
25567     },
25568
25569     success : function(response)
25570     {
25571         this.uploadComplete= true;
25572         if (this.haveProgress) {
25573             Roo.MessageBox.hide();
25574         }
25575         
25576         
25577         var result = this.processResponse(response);
25578         if(result === true || result.success){
25579             this.form.afterAction(this, true);
25580             return;
25581         }
25582         if(result.errors){
25583             this.form.markInvalid(result.errors);
25584             this.failureType = Roo.form.Action.SERVER_INVALID;
25585         }
25586         this.form.afterAction(this, false);
25587     },
25588     failure : function(response)
25589     {
25590         this.uploadComplete= true;
25591         if (this.haveProgress) {
25592             Roo.MessageBox.hide();
25593         }
25594         
25595         this.response = response;
25596         this.failureType = Roo.form.Action.CONNECT_FAILURE;
25597         this.form.afterAction(this, false);
25598     },
25599     
25600     handleResponse : function(response){
25601         if(this.form.errorReader){
25602             var rs = this.form.errorReader.read(response);
25603             var errors = [];
25604             if(rs.records){
25605                 for(var i = 0, len = rs.records.length; i < len; i++) {
25606                     var r = rs.records[i];
25607                     errors[i] = r.data;
25608                 }
25609             }
25610             if(errors.length < 1){
25611                 errors = null;
25612             }
25613             return {
25614                 success : rs.success,
25615                 errors : errors
25616             };
25617         }
25618         var ret = false;
25619         try {
25620             ret = Roo.decode(response.responseText);
25621         } catch (e) {
25622             ret = {
25623                 success: false,
25624                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
25625                 errors : []
25626             };
25627         }
25628         return ret;
25629         
25630     }
25631 });
25632
25633
25634 Roo.form.Action.Load = function(form, options){
25635     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
25636     this.reader = this.form.reader;
25637 };
25638
25639 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
25640     type : 'load',
25641
25642     run : function(){
25643         
25644         Roo.Ajax.request(Roo.apply(
25645                 this.createCallback(), {
25646                     method:this.getMethod(),
25647                     url:this.getUrl(false),
25648                     params:this.getParams()
25649         }));
25650     },
25651
25652     success : function(response){
25653         
25654         var result = this.processResponse(response);
25655         if(result === true || !result.success || !result.data){
25656             this.failureType = Roo.form.Action.LOAD_FAILURE;
25657             this.form.afterAction(this, false);
25658             return;
25659         }
25660         this.form.clearInvalid();
25661         this.form.setValues(result.data);
25662         this.form.afterAction(this, true);
25663     },
25664
25665     handleResponse : function(response){
25666         if(this.form.reader){
25667             var rs = this.form.reader.read(response);
25668             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
25669             return {
25670                 success : rs.success,
25671                 data : data
25672             };
25673         }
25674         return Roo.decode(response.responseText);
25675     }
25676 });
25677
25678 Roo.form.Action.ACTION_TYPES = {
25679     'load' : Roo.form.Action.Load,
25680     'submit' : Roo.form.Action.Submit
25681 };/*
25682  * Based on:
25683  * Ext JS Library 1.1.1
25684  * Copyright(c) 2006-2007, Ext JS, LLC.
25685  *
25686  * Originally Released Under LGPL - original licence link has changed is not relivant.
25687  *
25688  * Fork - LGPL
25689  * <script type="text/javascript">
25690  */
25691  
25692 /**
25693  * @class Roo.form.Layout
25694  * @extends Roo.Component
25695  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
25696  * @constructor
25697  * @param {Object} config Configuration options
25698  */
25699 Roo.form.Layout = function(config){
25700     var xitems = [];
25701     if (config.items) {
25702         xitems = config.items;
25703         delete config.items;
25704     }
25705     Roo.form.Layout.superclass.constructor.call(this, config);
25706     this.stack = [];
25707     Roo.each(xitems, this.addxtype, this);
25708      
25709 };
25710
25711 Roo.extend(Roo.form.Layout, Roo.Component, {
25712     /**
25713      * @cfg {String/Object} autoCreate
25714      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
25715      */
25716     /**
25717      * @cfg {String/Object/Function} style
25718      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
25719      * a function which returns such a specification.
25720      */
25721     /**
25722      * @cfg {String} labelAlign
25723      * Valid values are "left," "top" and "right" (defaults to "left")
25724      */
25725     /**
25726      * @cfg {Number} labelWidth
25727      * Fixed width in pixels of all field labels (defaults to undefined)
25728      */
25729     /**
25730      * @cfg {Boolean} clear
25731      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
25732      */
25733     clear : true,
25734     /**
25735      * @cfg {String} labelSeparator
25736      * The separator to use after field labels (defaults to ':')
25737      */
25738     labelSeparator : ':',
25739     /**
25740      * @cfg {Boolean} hideLabels
25741      * True to suppress the display of field labels in this layout (defaults to false)
25742      */
25743     hideLabels : false,
25744
25745     // private
25746     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
25747     
25748     isLayout : true,
25749     
25750     // private
25751     onRender : function(ct, position){
25752         if(this.el){ // from markup
25753             this.el = Roo.get(this.el);
25754         }else {  // generate
25755             var cfg = this.getAutoCreate();
25756             this.el = ct.createChild(cfg, position);
25757         }
25758         if(this.style){
25759             this.el.applyStyles(this.style);
25760         }
25761         if(this.labelAlign){
25762             this.el.addClass('x-form-label-'+this.labelAlign);
25763         }
25764         if(this.hideLabels){
25765             this.labelStyle = "display:none";
25766             this.elementStyle = "padding-left:0;";
25767         }else{
25768             if(typeof this.labelWidth == 'number'){
25769                 this.labelStyle = "width:"+this.labelWidth+"px;";
25770                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
25771             }
25772             if(this.labelAlign == 'top'){
25773                 this.labelStyle = "width:auto;";
25774                 this.elementStyle = "padding-left:0;";
25775             }
25776         }
25777         var stack = this.stack;
25778         var slen = stack.length;
25779         if(slen > 0){
25780             if(!this.fieldTpl){
25781                 var t = new Roo.Template(
25782                     '<div class="x-form-item {5}">',
25783                         '<label for="{0}" style="{2}">{1}{4}</label>',
25784                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
25785                         '</div>',
25786                     '</div><div class="x-form-clear-left"></div>'
25787                 );
25788                 t.disableFormats = true;
25789                 t.compile();
25790                 Roo.form.Layout.prototype.fieldTpl = t;
25791             }
25792             for(var i = 0; i < slen; i++) {
25793                 if(stack[i].isFormField){
25794                     this.renderField(stack[i]);
25795                 }else{
25796                     this.renderComponent(stack[i]);
25797                 }
25798             }
25799         }
25800         if(this.clear){
25801             this.el.createChild({cls:'x-form-clear'});
25802         }
25803     },
25804
25805     // private
25806     renderField : function(f){
25807         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
25808                f.id, //0
25809                f.fieldLabel, //1
25810                f.labelStyle||this.labelStyle||'', //2
25811                this.elementStyle||'', //3
25812                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
25813                f.itemCls||this.itemCls||''  //5
25814        ], true).getPrevSibling());
25815     },
25816
25817     // private
25818     renderComponent : function(c){
25819         c.render(c.isLayout ? this.el : this.el.createChild());    
25820     },
25821     /**
25822      * Adds a object form elements (using the xtype property as the factory method.)
25823      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
25824      * @param {Object} config 
25825      */
25826     addxtype : function(o)
25827     {
25828         // create the lement.
25829         o.form = this.form;
25830         var fe = Roo.factory(o, Roo.form);
25831         this.form.allItems.push(fe);
25832         this.stack.push(fe);
25833         
25834         if (fe.isFormField) {
25835             this.form.items.add(fe);
25836         }
25837          
25838         return fe;
25839     }
25840 });
25841
25842 /**
25843  * @class Roo.form.Column
25844  * @extends Roo.form.Layout
25845  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
25846  * @constructor
25847  * @param {Object} config Configuration options
25848  */
25849 Roo.form.Column = function(config){
25850     Roo.form.Column.superclass.constructor.call(this, config);
25851 };
25852
25853 Roo.extend(Roo.form.Column, Roo.form.Layout, {
25854     /**
25855      * @cfg {Number/String} width
25856      * The fixed width of the column in pixels or CSS value (defaults to "auto")
25857      */
25858     /**
25859      * @cfg {String/Object} autoCreate
25860      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
25861      */
25862
25863     // private
25864     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
25865
25866     // private
25867     onRender : function(ct, position){
25868         Roo.form.Column.superclass.onRender.call(this, ct, position);
25869         if(this.width){
25870             this.el.setWidth(this.width);
25871         }
25872     }
25873 });
25874
25875
25876 /**
25877  * @class Roo.form.Row
25878  * @extends Roo.form.Layout
25879  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
25880  * @constructor
25881  * @param {Object} config Configuration options
25882  */
25883
25884  
25885 Roo.form.Row = function(config){
25886     Roo.form.Row.superclass.constructor.call(this, config);
25887 };
25888  
25889 Roo.extend(Roo.form.Row, Roo.form.Layout, {
25890       /**
25891      * @cfg {Number/String} width
25892      * The fixed width of the column in pixels or CSS value (defaults to "auto")
25893      */
25894     /**
25895      * @cfg {Number/String} height
25896      * The fixed height of the column in pixels or CSS value (defaults to "auto")
25897      */
25898     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
25899     
25900     padWidth : 20,
25901     // private
25902     onRender : function(ct, position){
25903         //console.log('row render');
25904         if(!this.rowTpl){
25905             var t = new Roo.Template(
25906                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
25907                     '<label for="{0}" style="{2}">{1}{4}</label>',
25908                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
25909                     '</div>',
25910                 '</div>'
25911             );
25912             t.disableFormats = true;
25913             t.compile();
25914             Roo.form.Layout.prototype.rowTpl = t;
25915         }
25916         this.fieldTpl = this.rowTpl;
25917         
25918         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
25919         var labelWidth = 100;
25920         
25921         if ((this.labelAlign != 'top')) {
25922             if (typeof this.labelWidth == 'number') {
25923                 labelWidth = this.labelWidth
25924             }
25925             this.padWidth =  20 + labelWidth;
25926             
25927         }
25928         
25929         Roo.form.Column.superclass.onRender.call(this, ct, position);
25930         if(this.width){
25931             this.el.setWidth(this.width);
25932         }
25933         if(this.height){
25934             this.el.setHeight(this.height);
25935         }
25936     },
25937     
25938     // private
25939     renderField : function(f){
25940         f.fieldEl = this.fieldTpl.append(this.el, [
25941                f.id, f.fieldLabel,
25942                f.labelStyle||this.labelStyle||'',
25943                this.elementStyle||'',
25944                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
25945                f.itemCls||this.itemCls||'',
25946                f.width ? f.width + this.padWidth : 160 + this.padWidth
25947        ],true);
25948     }
25949 });
25950  
25951
25952 /**
25953  * @class Roo.form.FieldSet
25954  * @extends Roo.form.Layout
25955  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
25956  * @constructor
25957  * @param {Object} config Configuration options
25958  */
25959 Roo.form.FieldSet = function(config){
25960     Roo.form.FieldSet.superclass.constructor.call(this, config);
25961 };
25962
25963 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
25964     /**
25965      * @cfg {String} legend
25966      * The text to display as the legend for the FieldSet (defaults to '')
25967      */
25968     /**
25969      * @cfg {String/Object} autoCreate
25970      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
25971      */
25972
25973     // private
25974     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
25975
25976     // private
25977     onRender : function(ct, position){
25978         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
25979         if(this.legend){
25980             this.setLegend(this.legend);
25981         }
25982     },
25983
25984     // private
25985     setLegend : function(text){
25986         if(this.rendered){
25987             this.el.child('legend').update(text);
25988         }
25989     }
25990 });/*
25991  * Based on:
25992  * Ext JS Library 1.1.1
25993  * Copyright(c) 2006-2007, Ext JS, LLC.
25994  *
25995  * Originally Released Under LGPL - original licence link has changed is not relivant.
25996  *
25997  * Fork - LGPL
25998  * <script type="text/javascript">
25999  */
26000 /**
26001  * @class Roo.form.VTypes
26002  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
26003  * @singleton
26004  */
26005 Roo.form.VTypes = function(){
26006     // closure these in so they are only created once.
26007     var alpha = /^[a-zA-Z_]+$/;
26008     var alphanum = /^[a-zA-Z0-9_]+$/;
26009     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
26010     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
26011
26012     // All these messages and functions are configurable
26013     return {
26014         /**
26015          * The function used to validate email addresses
26016          * @param {String} value The email address
26017          */
26018         'email' : function(v){
26019             return email.test(v);
26020         },
26021         /**
26022          * The error text to display when the email validation function returns false
26023          * @type String
26024          */
26025         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
26026         /**
26027          * The keystroke filter mask to be applied on email input
26028          * @type RegExp
26029          */
26030         'emailMask' : /[a-z0-9_\.\-@]/i,
26031
26032         /**
26033          * The function used to validate URLs
26034          * @param {String} value The URL
26035          */
26036         'url' : function(v){
26037             return url.test(v);
26038         },
26039         /**
26040          * The error text to display when the url validation function returns false
26041          * @type String
26042          */
26043         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
26044         
26045         /**
26046          * The function used to validate alpha values
26047          * @param {String} value The value
26048          */
26049         'alpha' : function(v){
26050             return alpha.test(v);
26051         },
26052         /**
26053          * The error text to display when the alpha validation function returns false
26054          * @type String
26055          */
26056         'alphaText' : 'This field should only contain letters and _',
26057         /**
26058          * The keystroke filter mask to be applied on alpha input
26059          * @type RegExp
26060          */
26061         'alphaMask' : /[a-z_]/i,
26062
26063         /**
26064          * The function used to validate alphanumeric values
26065          * @param {String} value The value
26066          */
26067         'alphanum' : function(v){
26068             return alphanum.test(v);
26069         },
26070         /**
26071          * The error text to display when the alphanumeric validation function returns false
26072          * @type String
26073          */
26074         'alphanumText' : 'This field should only contain letters, numbers and _',
26075         /**
26076          * The keystroke filter mask to be applied on alphanumeric input
26077          * @type RegExp
26078          */
26079         'alphanumMask' : /[a-z0-9_]/i
26080     };
26081 }();//<script type="text/javascript">
26082
26083 /**
26084  * @class Roo.form.FCKeditor
26085  * @extends Roo.form.TextArea
26086  * Wrapper around the FCKEditor http://www.fckeditor.net
26087  * @constructor
26088  * Creates a new FCKeditor
26089  * @param {Object} config Configuration options
26090  */
26091 Roo.form.FCKeditor = function(config){
26092     Roo.form.FCKeditor.superclass.constructor.call(this, config);
26093     this.addEvents({
26094          /**
26095          * @event editorinit
26096          * Fired when the editor is initialized - you can add extra handlers here..
26097          * @param {FCKeditor} this
26098          * @param {Object} the FCK object.
26099          */
26100         editorinit : true
26101     });
26102     
26103     
26104 };
26105 Roo.form.FCKeditor.editors = { };
26106 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
26107 {
26108     //defaultAutoCreate : {
26109     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
26110     //},
26111     // private
26112     /**
26113      * @cfg {Object} fck options - see fck manual for details.
26114      */
26115     fckconfig : false,
26116     
26117     /**
26118      * @cfg {Object} fck toolbar set (Basic or Default)
26119      */
26120     toolbarSet : 'Basic',
26121     /**
26122      * @cfg {Object} fck BasePath
26123      */ 
26124     basePath : '/fckeditor/',
26125     
26126     
26127     frame : false,
26128     
26129     value : '',
26130     
26131    
26132     onRender : function(ct, position)
26133     {
26134         if(!this.el){
26135             this.defaultAutoCreate = {
26136                 tag: "textarea",
26137                 style:"width:300px;height:60px;",
26138                 autocomplete: "new-password"
26139             };
26140         }
26141         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
26142         /*
26143         if(this.grow){
26144             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
26145             if(this.preventScrollbars){
26146                 this.el.setStyle("overflow", "hidden");
26147             }
26148             this.el.setHeight(this.growMin);
26149         }
26150         */
26151         //console.log('onrender' + this.getId() );
26152         Roo.form.FCKeditor.editors[this.getId()] = this;
26153          
26154
26155         this.replaceTextarea() ;
26156         
26157     },
26158     
26159     getEditor : function() {
26160         return this.fckEditor;
26161     },
26162     /**
26163      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
26164      * @param {Mixed} value The value to set
26165      */
26166     
26167     
26168     setValue : function(value)
26169     {
26170         //console.log('setValue: ' + value);
26171         
26172         if(typeof(value) == 'undefined') { // not sure why this is happending...
26173             return;
26174         }
26175         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26176         
26177         //if(!this.el || !this.getEditor()) {
26178         //    this.value = value;
26179             //this.setValue.defer(100,this,[value]);    
26180         //    return;
26181         //} 
26182         
26183         if(!this.getEditor()) {
26184             return;
26185         }
26186         
26187         this.getEditor().SetData(value);
26188         
26189         //
26190
26191     },
26192
26193     /**
26194      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
26195      * @return {Mixed} value The field value
26196      */
26197     getValue : function()
26198     {
26199         
26200         if (this.frame && this.frame.dom.style.display == 'none') {
26201             return Roo.form.FCKeditor.superclass.getValue.call(this);
26202         }
26203         
26204         if(!this.el || !this.getEditor()) {
26205            
26206            // this.getValue.defer(100,this); 
26207             return this.value;
26208         }
26209        
26210         
26211         var value=this.getEditor().GetData();
26212         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26213         return Roo.form.FCKeditor.superclass.getValue.call(this);
26214         
26215
26216     },
26217
26218     /**
26219      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
26220      * @return {Mixed} value The field value
26221      */
26222     getRawValue : function()
26223     {
26224         if (this.frame && this.frame.dom.style.display == 'none') {
26225             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26226         }
26227         
26228         if(!this.el || !this.getEditor()) {
26229             //this.getRawValue.defer(100,this); 
26230             return this.value;
26231             return;
26232         }
26233         
26234         
26235         
26236         var value=this.getEditor().GetData();
26237         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
26238         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26239          
26240     },
26241     
26242     setSize : function(w,h) {
26243         
26244         
26245         
26246         //if (this.frame && this.frame.dom.style.display == 'none') {
26247         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26248         //    return;
26249         //}
26250         //if(!this.el || !this.getEditor()) {
26251         //    this.setSize.defer(100,this, [w,h]); 
26252         //    return;
26253         //}
26254         
26255         
26256         
26257         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26258         
26259         this.frame.dom.setAttribute('width', w);
26260         this.frame.dom.setAttribute('height', h);
26261         this.frame.setSize(w,h);
26262         
26263     },
26264     
26265     toggleSourceEdit : function(value) {
26266         
26267       
26268          
26269         this.el.dom.style.display = value ? '' : 'none';
26270         this.frame.dom.style.display = value ?  'none' : '';
26271         
26272     },
26273     
26274     
26275     focus: function(tag)
26276     {
26277         if (this.frame.dom.style.display == 'none') {
26278             return Roo.form.FCKeditor.superclass.focus.call(this);
26279         }
26280         if(!this.el || !this.getEditor()) {
26281             this.focus.defer(100,this, [tag]); 
26282             return;
26283         }
26284         
26285         
26286         
26287         
26288         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
26289         this.getEditor().Focus();
26290         if (tgs.length) {
26291             if (!this.getEditor().Selection.GetSelection()) {
26292                 this.focus.defer(100,this, [tag]); 
26293                 return;
26294             }
26295             
26296             
26297             var r = this.getEditor().EditorDocument.createRange();
26298             r.setStart(tgs[0],0);
26299             r.setEnd(tgs[0],0);
26300             this.getEditor().Selection.GetSelection().removeAllRanges();
26301             this.getEditor().Selection.GetSelection().addRange(r);
26302             this.getEditor().Focus();
26303         }
26304         
26305     },
26306     
26307     
26308     
26309     replaceTextarea : function()
26310     {
26311         if ( document.getElementById( this.getId() + '___Frame' ) ) {
26312             return ;
26313         }
26314         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
26315         //{
26316             // We must check the elements firstly using the Id and then the name.
26317         var oTextarea = document.getElementById( this.getId() );
26318         
26319         var colElementsByName = document.getElementsByName( this.getId() ) ;
26320          
26321         oTextarea.style.display = 'none' ;
26322
26323         if ( oTextarea.tabIndex ) {            
26324             this.TabIndex = oTextarea.tabIndex ;
26325         }
26326         
26327         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
26328         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
26329         this.frame = Roo.get(this.getId() + '___Frame')
26330     },
26331     
26332     _getConfigHtml : function()
26333     {
26334         var sConfig = '' ;
26335
26336         for ( var o in this.fckconfig ) {
26337             sConfig += sConfig.length > 0  ? '&amp;' : '';
26338             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
26339         }
26340
26341         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
26342     },
26343     
26344     
26345     _getIFrameHtml : function()
26346     {
26347         var sFile = 'fckeditor.html' ;
26348         /* no idea what this is about..
26349         try
26350         {
26351             if ( (/fcksource=true/i).test( window.top.location.search ) )
26352                 sFile = 'fckeditor.original.html' ;
26353         }
26354         catch (e) { 
26355         */
26356
26357         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
26358         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
26359         
26360         
26361         var html = '<iframe id="' + this.getId() +
26362             '___Frame" src="' + sLink +
26363             '" width="' + this.width +
26364             '" height="' + this.height + '"' +
26365             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
26366             ' frameborder="0" scrolling="no"></iframe>' ;
26367
26368         return html ;
26369     },
26370     
26371     _insertHtmlBefore : function( html, element )
26372     {
26373         if ( element.insertAdjacentHTML )       {
26374             // IE
26375             element.insertAdjacentHTML( 'beforeBegin', html ) ;
26376         } else { // Gecko
26377             var oRange = document.createRange() ;
26378             oRange.setStartBefore( element ) ;
26379             var oFragment = oRange.createContextualFragment( html );
26380             element.parentNode.insertBefore( oFragment, element ) ;
26381         }
26382     }
26383     
26384     
26385   
26386     
26387     
26388     
26389     
26390
26391 });
26392
26393 //Roo.reg('fckeditor', Roo.form.FCKeditor);
26394
26395 function FCKeditor_OnComplete(editorInstance){
26396     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
26397     f.fckEditor = editorInstance;
26398     //console.log("loaded");
26399     f.fireEvent('editorinit', f, editorInstance);
26400
26401   
26402
26403  
26404
26405
26406
26407
26408
26409
26410
26411
26412
26413
26414
26415
26416
26417
26418
26419 //<script type="text/javascript">
26420 /**
26421  * @class Roo.form.GridField
26422  * @extends Roo.form.Field
26423  * Embed a grid (or editable grid into a form)
26424  * STATUS ALPHA
26425  * 
26426  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
26427  * it needs 
26428  * xgrid.store = Roo.data.Store
26429  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
26430  * xgrid.store.reader = Roo.data.JsonReader 
26431  * 
26432  * 
26433  * @constructor
26434  * Creates a new GridField
26435  * @param {Object} config Configuration options
26436  */
26437 Roo.form.GridField = function(config){
26438     Roo.form.GridField.superclass.constructor.call(this, config);
26439      
26440 };
26441
26442 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
26443     /**
26444      * @cfg {Number} width  - used to restrict width of grid..
26445      */
26446     width : 100,
26447     /**
26448      * @cfg {Number} height - used to restrict height of grid..
26449      */
26450     height : 50,
26451      /**
26452      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
26453          * 
26454          *}
26455      */
26456     xgrid : false, 
26457     /**
26458      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
26459      * {tag: "input", type: "checkbox", autocomplete: "off"})
26460      */
26461    // defaultAutoCreate : { tag: 'div' },
26462     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
26463     /**
26464      * @cfg {String} addTitle Text to include for adding a title.
26465      */
26466     addTitle : false,
26467     //
26468     onResize : function(){
26469         Roo.form.Field.superclass.onResize.apply(this, arguments);
26470     },
26471
26472     initEvents : function(){
26473         // Roo.form.Checkbox.superclass.initEvents.call(this);
26474         // has no events...
26475        
26476     },
26477
26478
26479     getResizeEl : function(){
26480         return this.wrap;
26481     },
26482
26483     getPositionEl : function(){
26484         return this.wrap;
26485     },
26486
26487     // private
26488     onRender : function(ct, position){
26489         
26490         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
26491         var style = this.style;
26492         delete this.style;
26493         
26494         Roo.form.GridField.superclass.onRender.call(this, ct, position);
26495         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
26496         this.viewEl = this.wrap.createChild({ tag: 'div' });
26497         if (style) {
26498             this.viewEl.applyStyles(style);
26499         }
26500         if (this.width) {
26501             this.viewEl.setWidth(this.width);
26502         }
26503         if (this.height) {
26504             this.viewEl.setHeight(this.height);
26505         }
26506         //if(this.inputValue !== undefined){
26507         //this.setValue(this.value);
26508         
26509         
26510         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
26511         
26512         
26513         this.grid.render();
26514         this.grid.getDataSource().on('remove', this.refreshValue, this);
26515         this.grid.getDataSource().on('update', this.refreshValue, this);
26516         this.grid.on('afteredit', this.refreshValue, this);
26517  
26518     },
26519      
26520     
26521     /**
26522      * Sets the value of the item. 
26523      * @param {String} either an object  or a string..
26524      */
26525     setValue : function(v){
26526         //this.value = v;
26527         v = v || []; // empty set..
26528         // this does not seem smart - it really only affects memoryproxy grids..
26529         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
26530             var ds = this.grid.getDataSource();
26531             // assumes a json reader..
26532             var data = {}
26533             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
26534             ds.loadData( data);
26535         }
26536         // clear selection so it does not get stale.
26537         if (this.grid.sm) { 
26538             this.grid.sm.clearSelections();
26539         }
26540         
26541         Roo.form.GridField.superclass.setValue.call(this, v);
26542         this.refreshValue();
26543         // should load data in the grid really....
26544     },
26545     
26546     // private
26547     refreshValue: function() {
26548          var val = [];
26549         this.grid.getDataSource().each(function(r) {
26550             val.push(r.data);
26551         });
26552         this.el.dom.value = Roo.encode(val);
26553     }
26554     
26555      
26556     
26557     
26558 });/*
26559  * Based on:
26560  * Ext JS Library 1.1.1
26561  * Copyright(c) 2006-2007, Ext JS, LLC.
26562  *
26563  * Originally Released Under LGPL - original licence link has changed is not relivant.
26564  *
26565  * Fork - LGPL
26566  * <script type="text/javascript">
26567  */
26568 /**
26569  * @class Roo.form.DisplayField
26570  * @extends Roo.form.Field
26571  * A generic Field to display non-editable data.
26572  * @cfg {Boolean} closable (true|false) default false
26573  * @constructor
26574  * Creates a new Display Field item.
26575  * @param {Object} config Configuration options
26576  */
26577 Roo.form.DisplayField = function(config){
26578     Roo.form.DisplayField.superclass.constructor.call(this, config);
26579     
26580     this.addEvents({
26581         /**
26582          * @event close
26583          * Fires after the click the close btn
26584              * @param {Roo.form.DisplayField} this
26585              */
26586         close : true
26587     });
26588 };
26589
26590 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
26591     inputType:      'hidden',
26592     allowBlank:     true,
26593     readOnly:         true,
26594     
26595  
26596     /**
26597      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
26598      */
26599     focusClass : undefined,
26600     /**
26601      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
26602      */
26603     fieldClass: 'x-form-field',
26604     
26605      /**
26606      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
26607      */
26608     valueRenderer: undefined,
26609     
26610     width: 100,
26611     /**
26612      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
26613      * {tag: "input", type: "checkbox", autocomplete: "off"})
26614      */
26615      
26616  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
26617  
26618     closable : false,
26619     
26620     onResize : function(){
26621         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
26622         
26623     },
26624
26625     initEvents : function(){
26626         // Roo.form.Checkbox.superclass.initEvents.call(this);
26627         // has no events...
26628         
26629         if(this.closable){
26630             this.closeEl.on('click', this.onClose, this);
26631         }
26632        
26633     },
26634
26635
26636     getResizeEl : function(){
26637         return this.wrap;
26638     },
26639
26640     getPositionEl : function(){
26641         return this.wrap;
26642     },
26643
26644     // private
26645     onRender : function(ct, position){
26646         
26647         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
26648         //if(this.inputValue !== undefined){
26649         this.wrap = this.el.wrap();
26650         
26651         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
26652         
26653         if(this.closable){
26654             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
26655         }
26656         
26657         if (this.bodyStyle) {
26658             this.viewEl.applyStyles(this.bodyStyle);
26659         }
26660         //this.viewEl.setStyle('padding', '2px');
26661         
26662         this.setValue(this.value);
26663         
26664     },
26665 /*
26666     // private
26667     initValue : Roo.emptyFn,
26668
26669   */
26670
26671         // private
26672     onClick : function(){
26673         
26674     },
26675
26676     /**
26677      * Sets the checked state of the checkbox.
26678      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
26679      */
26680     setValue : function(v){
26681         this.value = v;
26682         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
26683         // this might be called before we have a dom element..
26684         if (!this.viewEl) {
26685             return;
26686         }
26687         this.viewEl.dom.innerHTML = html;
26688         Roo.form.DisplayField.superclass.setValue.call(this, v);
26689
26690     },
26691     
26692     onClose : function(e)
26693     {
26694         e.preventDefault();
26695         
26696         this.fireEvent('close', this);
26697     }
26698 });/*
26699  * 
26700  * Licence- LGPL
26701  * 
26702  */
26703
26704 /**
26705  * @class Roo.form.DayPicker
26706  * @extends Roo.form.Field
26707  * A Day picker show [M] [T] [W] ....
26708  * @constructor
26709  * Creates a new Day Picker
26710  * @param {Object} config Configuration options
26711  */
26712 Roo.form.DayPicker= function(config){
26713     Roo.form.DayPicker.superclass.constructor.call(this, config);
26714      
26715 };
26716
26717 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
26718     /**
26719      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
26720      */
26721     focusClass : undefined,
26722     /**
26723      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
26724      */
26725     fieldClass: "x-form-field",
26726    
26727     /**
26728      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
26729      * {tag: "input", type: "checkbox", autocomplete: "off"})
26730      */
26731     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
26732     
26733    
26734     actionMode : 'viewEl', 
26735     //
26736     // private
26737  
26738     inputType : 'hidden',
26739     
26740      
26741     inputElement: false, // real input element?
26742     basedOn: false, // ????
26743     
26744     isFormField: true, // not sure where this is needed!!!!
26745
26746     onResize : function(){
26747         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
26748         if(!this.boxLabel){
26749             this.el.alignTo(this.wrap, 'c-c');
26750         }
26751     },
26752
26753     initEvents : function(){
26754         Roo.form.Checkbox.superclass.initEvents.call(this);
26755         this.el.on("click", this.onClick,  this);
26756         this.el.on("change", this.onClick,  this);
26757     },
26758
26759
26760     getResizeEl : function(){
26761         return this.wrap;
26762     },
26763
26764     getPositionEl : function(){
26765         return this.wrap;
26766     },
26767
26768     
26769     // private
26770     onRender : function(ct, position){
26771         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
26772        
26773         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
26774         
26775         var r1 = '<table><tr>';
26776         var r2 = '<tr class="x-form-daypick-icons">';
26777         for (var i=0; i < 7; i++) {
26778             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
26779             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
26780         }
26781         
26782         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
26783         viewEl.select('img').on('click', this.onClick, this);
26784         this.viewEl = viewEl;   
26785         
26786         
26787         // this will not work on Chrome!!!
26788         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
26789         this.el.on('propertychange', this.setFromHidden,  this);  //ie
26790         
26791         
26792           
26793
26794     },
26795
26796     // private
26797     initValue : Roo.emptyFn,
26798
26799     /**
26800      * Returns the checked state of the checkbox.
26801      * @return {Boolean} True if checked, else false
26802      */
26803     getValue : function(){
26804         return this.el.dom.value;
26805         
26806     },
26807
26808         // private
26809     onClick : function(e){ 
26810         //this.setChecked(!this.checked);
26811         Roo.get(e.target).toggleClass('x-menu-item-checked');
26812         this.refreshValue();
26813         //if(this.el.dom.checked != this.checked){
26814         //    this.setValue(this.el.dom.checked);
26815        // }
26816     },
26817     
26818     // private
26819     refreshValue : function()
26820     {
26821         var val = '';
26822         this.viewEl.select('img',true).each(function(e,i,n)  {
26823             val += e.is(".x-menu-item-checked") ? String(n) : '';
26824         });
26825         this.setValue(val, true);
26826     },
26827
26828     /**
26829      * Sets the checked state of the checkbox.
26830      * On is always based on a string comparison between inputValue and the param.
26831      * @param {Boolean/String} value - the value to set 
26832      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
26833      */
26834     setValue : function(v,suppressEvent){
26835         if (!this.el.dom) {
26836             return;
26837         }
26838         var old = this.el.dom.value ;
26839         this.el.dom.value = v;
26840         if (suppressEvent) {
26841             return ;
26842         }
26843          
26844         // update display..
26845         this.viewEl.select('img',true).each(function(e,i,n)  {
26846             
26847             var on = e.is(".x-menu-item-checked");
26848             var newv = v.indexOf(String(n)) > -1;
26849             if (on != newv) {
26850                 e.toggleClass('x-menu-item-checked');
26851             }
26852             
26853         });
26854         
26855         
26856         this.fireEvent('change', this, v, old);
26857         
26858         
26859     },
26860    
26861     // handle setting of hidden value by some other method!!?!?
26862     setFromHidden: function()
26863     {
26864         if(!this.el){
26865             return;
26866         }
26867         //console.log("SET FROM HIDDEN");
26868         //alert('setFrom hidden');
26869         this.setValue(this.el.dom.value);
26870     },
26871     
26872     onDestroy : function()
26873     {
26874         if(this.viewEl){
26875             Roo.get(this.viewEl).remove();
26876         }
26877          
26878         Roo.form.DayPicker.superclass.onDestroy.call(this);
26879     }
26880
26881 });/*
26882  * RooJS Library 1.1.1
26883  * Copyright(c) 2008-2011  Alan Knowles
26884  *
26885  * License - LGPL
26886  */
26887  
26888
26889 /**
26890  * @class Roo.form.ComboCheck
26891  * @extends Roo.form.ComboBox
26892  * A combobox for multiple select items.
26893  *
26894  * FIXME - could do with a reset button..
26895  * 
26896  * @constructor
26897  * Create a new ComboCheck
26898  * @param {Object} config Configuration options
26899  */
26900 Roo.form.ComboCheck = function(config){
26901     Roo.form.ComboCheck.superclass.constructor.call(this, config);
26902     // should verify some data...
26903     // like
26904     // hiddenName = required..
26905     // displayField = required
26906     // valudField == required
26907     var req= [ 'hiddenName', 'displayField', 'valueField' ];
26908     var _t = this;
26909     Roo.each(req, function(e) {
26910         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
26911             throw "Roo.form.ComboCheck : missing value for: " + e;
26912         }
26913     });
26914     
26915     
26916 };
26917
26918 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
26919      
26920      
26921     editable : false,
26922      
26923     selectedClass: 'x-menu-item-checked', 
26924     
26925     // private
26926     onRender : function(ct, position){
26927         var _t = this;
26928         
26929         
26930         
26931         if(!this.tpl){
26932             var cls = 'x-combo-list';
26933
26934             
26935             this.tpl =  new Roo.Template({
26936                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
26937                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
26938                    '<span>{' + this.displayField + '}</span>' +
26939                     '</div>' 
26940                 
26941             });
26942         }
26943  
26944         
26945         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
26946         this.view.singleSelect = false;
26947         this.view.multiSelect = true;
26948         this.view.toggleSelect = true;
26949         this.pageTb.add(new Roo.Toolbar.Fill(), {
26950             
26951             text: 'Done',
26952             handler: function()
26953             {
26954                 _t.collapse();
26955             }
26956         });
26957     },
26958     
26959     onViewOver : function(e, t){
26960         // do nothing...
26961         return;
26962         
26963     },
26964     
26965     onViewClick : function(doFocus,index){
26966         return;
26967         
26968     },
26969     select: function () {
26970         //Roo.log("SELECT CALLED");
26971     },
26972      
26973     selectByValue : function(xv, scrollIntoView){
26974         var ar = this.getValueArray();
26975         var sels = [];
26976         
26977         Roo.each(ar, function(v) {
26978             if(v === undefined || v === null){
26979                 return;
26980             }
26981             var r = this.findRecord(this.valueField, v);
26982             if(r){
26983                 sels.push(this.store.indexOf(r))
26984                 
26985             }
26986         },this);
26987         this.view.select(sels);
26988         return false;
26989     },
26990     
26991     
26992     
26993     onSelect : function(record, index){
26994        // Roo.log("onselect Called");
26995        // this is only called by the clear button now..
26996         this.view.clearSelections();
26997         this.setValue('[]');
26998         if (this.value != this.valueBefore) {
26999             this.fireEvent('change', this, this.value, this.valueBefore);
27000             this.valueBefore = this.value;
27001         }
27002     },
27003     getValueArray : function()
27004     {
27005         var ar = [] ;
27006         
27007         try {
27008             //Roo.log(this.value);
27009             if (typeof(this.value) == 'undefined') {
27010                 return [];
27011             }
27012             var ar = Roo.decode(this.value);
27013             return  ar instanceof Array ? ar : []; //?? valid?
27014             
27015         } catch(e) {
27016             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
27017             return [];
27018         }
27019          
27020     },
27021     expand : function ()
27022     {
27023         
27024         Roo.form.ComboCheck.superclass.expand.call(this);
27025         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
27026         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
27027         
27028
27029     },
27030     
27031     collapse : function(){
27032         Roo.form.ComboCheck.superclass.collapse.call(this);
27033         var sl = this.view.getSelectedIndexes();
27034         var st = this.store;
27035         var nv = [];
27036         var tv = [];
27037         var r;
27038         Roo.each(sl, function(i) {
27039             r = st.getAt(i);
27040             nv.push(r.get(this.valueField));
27041         },this);
27042         this.setValue(Roo.encode(nv));
27043         if (this.value != this.valueBefore) {
27044
27045             this.fireEvent('change', this, this.value, this.valueBefore);
27046             this.valueBefore = this.value;
27047         }
27048         
27049     },
27050     
27051     setValue : function(v){
27052         // Roo.log(v);
27053         this.value = v;
27054         
27055         var vals = this.getValueArray();
27056         var tv = [];
27057         Roo.each(vals, function(k) {
27058             var r = this.findRecord(this.valueField, k);
27059             if(r){
27060                 tv.push(r.data[this.displayField]);
27061             }else if(this.valueNotFoundText !== undefined){
27062                 tv.push( this.valueNotFoundText );
27063             }
27064         },this);
27065        // Roo.log(tv);
27066         
27067         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
27068         this.hiddenField.value = v;
27069         this.value = v;
27070     }
27071     
27072 });/*
27073  * Based on:
27074  * Ext JS Library 1.1.1
27075  * Copyright(c) 2006-2007, Ext JS, LLC.
27076  *
27077  * Originally Released Under LGPL - original licence link has changed is not relivant.
27078  *
27079  * Fork - LGPL
27080  * <script type="text/javascript">
27081  */
27082  
27083 /**
27084  * @class Roo.form.Signature
27085  * @extends Roo.form.Field
27086  * Signature field.  
27087  * @constructor
27088  * 
27089  * @param {Object} config Configuration options
27090  */
27091
27092 Roo.form.Signature = function(config){
27093     Roo.form.Signature.superclass.constructor.call(this, config);
27094     
27095     this.addEvents({// not in used??
27096          /**
27097          * @event confirm
27098          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
27099              * @param {Roo.form.Signature} combo This combo box
27100              */
27101         'confirm' : true,
27102         /**
27103          * @event reset
27104          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
27105              * @param {Roo.form.ComboBox} combo This combo box
27106              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
27107              */
27108         'reset' : true
27109     });
27110 };
27111
27112 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
27113     /**
27114      * @cfg {Object} labels Label to use when rendering a form.
27115      * defaults to 
27116      * labels : { 
27117      *      clear : "Clear",
27118      *      confirm : "Confirm"
27119      *  }
27120      */
27121     labels : { 
27122         clear : "Clear",
27123         confirm : "Confirm"
27124     },
27125     /**
27126      * @cfg {Number} width The signature panel width (defaults to 300)
27127      */
27128     width: 300,
27129     /**
27130      * @cfg {Number} height The signature panel height (defaults to 100)
27131      */
27132     height : 100,
27133     /**
27134      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
27135      */
27136     allowBlank : false,
27137     
27138     //private
27139     // {Object} signPanel The signature SVG panel element (defaults to {})
27140     signPanel : {},
27141     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
27142     isMouseDown : false,
27143     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
27144     isConfirmed : false,
27145     // {String} signatureTmp SVG mapping string (defaults to empty string)
27146     signatureTmp : '',
27147     
27148     
27149     defaultAutoCreate : { // modified by initCompnoent..
27150         tag: "input",
27151         type:"hidden"
27152     },
27153
27154     // private
27155     onRender : function(ct, position){
27156         
27157         Roo.form.Signature.superclass.onRender.call(this, ct, position);
27158         
27159         this.wrap = this.el.wrap({
27160             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
27161         });
27162         
27163         this.createToolbar(this);
27164         this.signPanel = this.wrap.createChild({
27165                 tag: 'div',
27166                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
27167             }, this.el
27168         );
27169             
27170         this.svgID = Roo.id();
27171         this.svgEl = this.signPanel.createChild({
27172               xmlns : 'http://www.w3.org/2000/svg',
27173               tag : 'svg',
27174               id : this.svgID + "-svg",
27175               width: this.width,
27176               height: this.height,
27177               viewBox: '0 0 '+this.width+' '+this.height,
27178               cn : [
27179                 {
27180                     tag: "rect",
27181                     id: this.svgID + "-svg-r",
27182                     width: this.width,
27183                     height: this.height,
27184                     fill: "#ffa"
27185                 },
27186                 {
27187                     tag: "line",
27188                     id: this.svgID + "-svg-l",
27189                     x1: "0", // start
27190                     y1: (this.height*0.8), // start set the line in 80% of height
27191                     x2: this.width, // end
27192                     y2: (this.height*0.8), // end set the line in 80% of height
27193                     'stroke': "#666",
27194                     'stroke-width': "1",
27195                     'stroke-dasharray': "3",
27196                     'shape-rendering': "crispEdges",
27197                     'pointer-events': "none"
27198                 },
27199                 {
27200                     tag: "path",
27201                     id: this.svgID + "-svg-p",
27202                     'stroke': "navy",
27203                     'stroke-width': "3",
27204                     'fill': "none",
27205                     'pointer-events': 'none'
27206                 }
27207               ]
27208         });
27209         this.createSVG();
27210         this.svgBox = this.svgEl.dom.getScreenCTM();
27211     },
27212     createSVG : function(){ 
27213         var svg = this.signPanel;
27214         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
27215         var t = this;
27216
27217         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
27218         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
27219         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
27220         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
27221         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
27222         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
27223         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
27224         
27225     },
27226     isTouchEvent : function(e){
27227         return e.type.match(/^touch/);
27228     },
27229     getCoords : function (e) {
27230         var pt    = this.svgEl.dom.createSVGPoint();
27231         pt.x = e.clientX; 
27232         pt.y = e.clientY;
27233         if (this.isTouchEvent(e)) {
27234             pt.x =  e.targetTouches[0].clientX;
27235             pt.y = e.targetTouches[0].clientY;
27236         }
27237         var a = this.svgEl.dom.getScreenCTM();
27238         var b = a.inverse();
27239         var mx = pt.matrixTransform(b);
27240         return mx.x + ',' + mx.y;
27241     },
27242     //mouse event headler 
27243     down : function (e) {
27244         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
27245         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
27246         
27247         this.isMouseDown = true;
27248         
27249         e.preventDefault();
27250     },
27251     move : function (e) {
27252         if (this.isMouseDown) {
27253             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
27254             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
27255         }
27256         
27257         e.preventDefault();
27258     },
27259     up : function (e) {
27260         this.isMouseDown = false;
27261         var sp = this.signatureTmp.split(' ');
27262         
27263         if(sp.length > 1){
27264             if(!sp[sp.length-2].match(/^L/)){
27265                 sp.pop();
27266                 sp.pop();
27267                 sp.push("");
27268                 this.signatureTmp = sp.join(" ");
27269             }
27270         }
27271         if(this.getValue() != this.signatureTmp){
27272             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27273             this.isConfirmed = false;
27274         }
27275         e.preventDefault();
27276     },
27277     
27278     /**
27279      * Protected method that will not generally be called directly. It
27280      * is called when the editor creates its toolbar. Override this method if you need to
27281      * add custom toolbar buttons.
27282      * @param {HtmlEditor} editor
27283      */
27284     createToolbar : function(editor){
27285          function btn(id, toggle, handler){
27286             var xid = fid + '-'+ id ;
27287             return {
27288                 id : xid,
27289                 cmd : id,
27290                 cls : 'x-btn-icon x-edit-'+id,
27291                 enableToggle:toggle !== false,
27292                 scope: editor, // was editor...
27293                 handler:handler||editor.relayBtnCmd,
27294                 clickEvent:'mousedown',
27295                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27296                 tabIndex:-1
27297             };
27298         }
27299         
27300         
27301         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
27302         this.tb = tb;
27303         this.tb.add(
27304            {
27305                 cls : ' x-signature-btn x-signature-'+id,
27306                 scope: editor, // was editor...
27307                 handler: this.reset,
27308                 clickEvent:'mousedown',
27309                 text: this.labels.clear
27310             },
27311             {
27312                  xtype : 'Fill',
27313                  xns: Roo.Toolbar
27314             }, 
27315             {
27316                 cls : '  x-signature-btn x-signature-'+id,
27317                 scope: editor, // was editor...
27318                 handler: this.confirmHandler,
27319                 clickEvent:'mousedown',
27320                 text: this.labels.confirm
27321             }
27322         );
27323     
27324     },
27325     //public
27326     /**
27327      * when user is clicked confirm then show this image.....
27328      * 
27329      * @return {String} Image Data URI
27330      */
27331     getImageDataURI : function(){
27332         var svg = this.svgEl.dom.parentNode.innerHTML;
27333         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
27334         return src; 
27335     },
27336     /**
27337      * 
27338      * @return {Boolean} this.isConfirmed
27339      */
27340     getConfirmed : function(){
27341         return this.isConfirmed;
27342     },
27343     /**
27344      * 
27345      * @return {Number} this.width
27346      */
27347     getWidth : function(){
27348         return this.width;
27349     },
27350     /**
27351      * 
27352      * @return {Number} this.height
27353      */
27354     getHeight : function(){
27355         return this.height;
27356     },
27357     // private
27358     getSignature : function(){
27359         return this.signatureTmp;
27360     },
27361     // private
27362     reset : function(){
27363         this.signatureTmp = '';
27364         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27365         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
27366         this.isConfirmed = false;
27367         Roo.form.Signature.superclass.reset.call(this);
27368     },
27369     setSignature : function(s){
27370         this.signatureTmp = s;
27371         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27372         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
27373         this.setValue(s);
27374         this.isConfirmed = false;
27375         Roo.form.Signature.superclass.reset.call(this);
27376     }, 
27377     test : function(){
27378 //        Roo.log(this.signPanel.dom.contentWindow.up())
27379     },
27380     //private
27381     setConfirmed : function(){
27382         
27383         
27384         
27385 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
27386     },
27387     // private
27388     confirmHandler : function(){
27389         if(!this.getSignature()){
27390             return;
27391         }
27392         
27393         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
27394         this.setValue(this.getSignature());
27395         this.isConfirmed = true;
27396         
27397         this.fireEvent('confirm', this);
27398     },
27399     // private
27400     // Subclasses should provide the validation implementation by overriding this
27401     validateValue : function(value){
27402         if(this.allowBlank){
27403             return true;
27404         }
27405         
27406         if(this.isConfirmed){
27407             return true;
27408         }
27409         return false;
27410     }
27411 });/*
27412  * Based on:
27413  * Ext JS Library 1.1.1
27414  * Copyright(c) 2006-2007, Ext JS, LLC.
27415  *
27416  * Originally Released Under LGPL - original licence link has changed is not relivant.
27417  *
27418  * Fork - LGPL
27419  * <script type="text/javascript">
27420  */
27421  
27422
27423 /**
27424  * @class Roo.form.ComboBox
27425  * @extends Roo.form.TriggerField
27426  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
27427  * @constructor
27428  * Create a new ComboBox.
27429  * @param {Object} config Configuration options
27430  */
27431 Roo.form.Select = function(config){
27432     Roo.form.Select.superclass.constructor.call(this, config);
27433      
27434 };
27435
27436 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
27437     /**
27438      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
27439      */
27440     /**
27441      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
27442      * rendering into an Roo.Editor, defaults to false)
27443      */
27444     /**
27445      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
27446      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
27447      */
27448     /**
27449      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
27450      */
27451     /**
27452      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
27453      * the dropdown list (defaults to undefined, with no header element)
27454      */
27455
27456      /**
27457      * @cfg {String/Roo.Template} tpl The template to use to render the output
27458      */
27459      
27460     // private
27461     defaultAutoCreate : {tag: "select"  },
27462     /**
27463      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
27464      */
27465     listWidth: undefined,
27466     /**
27467      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
27468      * mode = 'remote' or 'text' if mode = 'local')
27469      */
27470     displayField: undefined,
27471     /**
27472      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
27473      * mode = 'remote' or 'value' if mode = 'local'). 
27474      * Note: use of a valueField requires the user make a selection
27475      * in order for a value to be mapped.
27476      */
27477     valueField: undefined,
27478     
27479     
27480     /**
27481      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
27482      * field's data value (defaults to the underlying DOM element's name)
27483      */
27484     hiddenName: undefined,
27485     /**
27486      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
27487      */
27488     listClass: '',
27489     /**
27490      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
27491      */
27492     selectedClass: 'x-combo-selected',
27493     /**
27494      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
27495      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
27496      * which displays a downward arrow icon).
27497      */
27498     triggerClass : 'x-form-arrow-trigger',
27499     /**
27500      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
27501      */
27502     shadow:'sides',
27503     /**
27504      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
27505      * anchor positions (defaults to 'tl-bl')
27506      */
27507     listAlign: 'tl-bl?',
27508     /**
27509      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
27510      */
27511     maxHeight: 300,
27512     /**
27513      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
27514      * query specified by the allQuery config option (defaults to 'query')
27515      */
27516     triggerAction: 'query',
27517     /**
27518      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
27519      * (defaults to 4, does not apply if editable = false)
27520      */
27521     minChars : 4,
27522     /**
27523      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
27524      * delay (typeAheadDelay) if it matches a known value (defaults to false)
27525      */
27526     typeAhead: false,
27527     /**
27528      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
27529      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
27530      */
27531     queryDelay: 500,
27532     /**
27533      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
27534      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
27535      */
27536     pageSize: 0,
27537     /**
27538      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
27539      * when editable = true (defaults to false)
27540      */
27541     selectOnFocus:false,
27542     /**
27543      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
27544      */
27545     queryParam: 'query',
27546     /**
27547      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
27548      * when mode = 'remote' (defaults to 'Loading...')
27549      */
27550     loadingText: 'Loading...',
27551     /**
27552      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
27553      */
27554     resizable: false,
27555     /**
27556      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
27557      */
27558     handleHeight : 8,
27559     /**
27560      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
27561      * traditional select (defaults to true)
27562      */
27563     editable: true,
27564     /**
27565      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
27566      */
27567     allQuery: '',
27568     /**
27569      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
27570      */
27571     mode: 'remote',
27572     /**
27573      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
27574      * listWidth has a higher value)
27575      */
27576     minListWidth : 70,
27577     /**
27578      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
27579      * allow the user to set arbitrary text into the field (defaults to false)
27580      */
27581     forceSelection:false,
27582     /**
27583      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
27584      * if typeAhead = true (defaults to 250)
27585      */
27586     typeAheadDelay : 250,
27587     /**
27588      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
27589      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
27590      */
27591     valueNotFoundText : undefined,
27592     
27593     /**
27594      * @cfg {String} defaultValue The value displayed after loading the store.
27595      */
27596     defaultValue: '',
27597     
27598     /**
27599      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
27600      */
27601     blockFocus : false,
27602     
27603     /**
27604      * @cfg {Boolean} disableClear Disable showing of clear button.
27605      */
27606     disableClear : false,
27607     /**
27608      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
27609      */
27610     alwaysQuery : false,
27611     
27612     //private
27613     addicon : false,
27614     editicon: false,
27615     
27616     // element that contains real text value.. (when hidden is used..)
27617      
27618     // private
27619     onRender : function(ct, position){
27620         Roo.form.Field.prototype.onRender.call(this, ct, position);
27621         
27622         if(this.store){
27623             this.store.on('beforeload', this.onBeforeLoad, this);
27624             this.store.on('load', this.onLoad, this);
27625             this.store.on('loadexception', this.onLoadException, this);
27626             this.store.load({});
27627         }
27628         
27629         
27630         
27631     },
27632
27633     // private
27634     initEvents : function(){
27635         //Roo.form.ComboBox.superclass.initEvents.call(this);
27636  
27637     },
27638
27639     onDestroy : function(){
27640        
27641         if(this.store){
27642             this.store.un('beforeload', this.onBeforeLoad, this);
27643             this.store.un('load', this.onLoad, this);
27644             this.store.un('loadexception', this.onLoadException, this);
27645         }
27646         //Roo.form.ComboBox.superclass.onDestroy.call(this);
27647     },
27648
27649     // private
27650     fireKey : function(e){
27651         if(e.isNavKeyPress() && !this.list.isVisible()){
27652             this.fireEvent("specialkey", this, e);
27653         }
27654     },
27655
27656     // private
27657     onResize: function(w, h){
27658         
27659         return; 
27660     
27661         
27662     },
27663
27664     /**
27665      * Allow or prevent the user from directly editing the field text.  If false is passed,
27666      * the user will only be able to select from the items defined in the dropdown list.  This method
27667      * is the runtime equivalent of setting the 'editable' config option at config time.
27668      * @param {Boolean} value True to allow the user to directly edit the field text
27669      */
27670     setEditable : function(value){
27671          
27672     },
27673
27674     // private
27675     onBeforeLoad : function(){
27676         
27677         Roo.log("Select before load");
27678         return;
27679     
27680         this.innerList.update(this.loadingText ?
27681                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
27682         //this.restrictHeight();
27683         this.selectedIndex = -1;
27684     },
27685
27686     // private
27687     onLoad : function(){
27688
27689     
27690         var dom = this.el.dom;
27691         dom.innerHTML = '';
27692          var od = dom.ownerDocument;
27693          
27694         if (this.emptyText) {
27695             var op = od.createElement('option');
27696             op.setAttribute('value', '');
27697             op.innerHTML = String.format('{0}', this.emptyText);
27698             dom.appendChild(op);
27699         }
27700         if(this.store.getCount() > 0){
27701            
27702             var vf = this.valueField;
27703             var df = this.displayField;
27704             this.store.data.each(function(r) {
27705                 // which colmsn to use... testing - cdoe / title..
27706                 var op = od.createElement('option');
27707                 op.setAttribute('value', r.data[vf]);
27708                 op.innerHTML = String.format('{0}', r.data[df]);
27709                 dom.appendChild(op);
27710             });
27711             if (typeof(this.defaultValue != 'undefined')) {
27712                 this.setValue(this.defaultValue);
27713             }
27714             
27715              
27716         }else{
27717             //this.onEmptyResults();
27718         }
27719         //this.el.focus();
27720     },
27721     // private
27722     onLoadException : function()
27723     {
27724         dom.innerHTML = '';
27725             
27726         Roo.log("Select on load exception");
27727         return;
27728     
27729         this.collapse();
27730         Roo.log(this.store.reader.jsonData);
27731         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
27732             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
27733         }
27734         
27735         
27736     },
27737     // private
27738     onTypeAhead : function(){
27739          
27740     },
27741
27742     // private
27743     onSelect : function(record, index){
27744         Roo.log('on select?');
27745         return;
27746         if(this.fireEvent('beforeselect', this, record, index) !== false){
27747             this.setFromData(index > -1 ? record.data : false);
27748             this.collapse();
27749             this.fireEvent('select', this, record, index);
27750         }
27751     },
27752
27753     /**
27754      * Returns the currently selected field value or empty string if no value is set.
27755      * @return {String} value The selected value
27756      */
27757     getValue : function(){
27758         var dom = this.el.dom;
27759         this.value = dom.options[dom.selectedIndex].value;
27760         return this.value;
27761         
27762     },
27763
27764     /**
27765      * Clears any text/value currently set in the field
27766      */
27767     clearValue : function(){
27768         this.value = '';
27769         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
27770         
27771     },
27772
27773     /**
27774      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
27775      * will be displayed in the field.  If the value does not match the data value of an existing item,
27776      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
27777      * Otherwise the field will be blank (although the value will still be set).
27778      * @param {String} value The value to match
27779      */
27780     setValue : function(v){
27781         var d = this.el.dom;
27782         for (var i =0; i < d.options.length;i++) {
27783             if (v == d.options[i].value) {
27784                 d.selectedIndex = i;
27785                 this.value = v;
27786                 return;
27787             }
27788         }
27789         this.clearValue();
27790     },
27791     /**
27792      * @property {Object} the last set data for the element
27793      */
27794     
27795     lastData : false,
27796     /**
27797      * Sets the value of the field based on a object which is related to the record format for the store.
27798      * @param {Object} value the value to set as. or false on reset?
27799      */
27800     setFromData : function(o){
27801         Roo.log('setfrom data?');
27802          
27803         
27804         
27805     },
27806     // private
27807     reset : function(){
27808         this.clearValue();
27809     },
27810     // private
27811     findRecord : function(prop, value){
27812         
27813         return false;
27814     
27815         var record;
27816         if(this.store.getCount() > 0){
27817             this.store.each(function(r){
27818                 if(r.data[prop] == value){
27819                     record = r;
27820                     return false;
27821                 }
27822                 return true;
27823             });
27824         }
27825         return record;
27826     },
27827     
27828     getName: function()
27829     {
27830         // returns hidden if it's set..
27831         if (!this.rendered) {return ''};
27832         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
27833         
27834     },
27835      
27836
27837     
27838
27839     // private
27840     onEmptyResults : function(){
27841         Roo.log('empty results');
27842         //this.collapse();
27843     },
27844
27845     /**
27846      * Returns true if the dropdown list is expanded, else false.
27847      */
27848     isExpanded : function(){
27849         return false;
27850     },
27851
27852     /**
27853      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
27854      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
27855      * @param {String} value The data value of the item to select
27856      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
27857      * selected item if it is not currently in view (defaults to true)
27858      * @return {Boolean} True if the value matched an item in the list, else false
27859      */
27860     selectByValue : function(v, scrollIntoView){
27861         Roo.log('select By Value');
27862         return false;
27863     
27864         if(v !== undefined && v !== null){
27865             var r = this.findRecord(this.valueField || this.displayField, v);
27866             if(r){
27867                 this.select(this.store.indexOf(r), scrollIntoView);
27868                 return true;
27869             }
27870         }
27871         return false;
27872     },
27873
27874     /**
27875      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
27876      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
27877      * @param {Number} index The zero-based index of the list item to select
27878      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
27879      * selected item if it is not currently in view (defaults to true)
27880      */
27881     select : function(index, scrollIntoView){
27882         Roo.log('select ');
27883         return  ;
27884         
27885         this.selectedIndex = index;
27886         this.view.select(index);
27887         if(scrollIntoView !== false){
27888             var el = this.view.getNode(index);
27889             if(el){
27890                 this.innerList.scrollChildIntoView(el, false);
27891             }
27892         }
27893     },
27894
27895       
27896
27897     // private
27898     validateBlur : function(){
27899         
27900         return;
27901         
27902     },
27903
27904     // private
27905     initQuery : function(){
27906         this.doQuery(this.getRawValue());
27907     },
27908
27909     // private
27910     doForce : function(){
27911         if(this.el.dom.value.length > 0){
27912             this.el.dom.value =
27913                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
27914              
27915         }
27916     },
27917
27918     /**
27919      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
27920      * query allowing the query action to be canceled if needed.
27921      * @param {String} query The SQL query to execute
27922      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
27923      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
27924      * saved in the current store (defaults to false)
27925      */
27926     doQuery : function(q, forceAll){
27927         
27928         Roo.log('doQuery?');
27929         if(q === undefined || q === null){
27930             q = '';
27931         }
27932         var qe = {
27933             query: q,
27934             forceAll: forceAll,
27935             combo: this,
27936             cancel:false
27937         };
27938         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
27939             return false;
27940         }
27941         q = qe.query;
27942         forceAll = qe.forceAll;
27943         if(forceAll === true || (q.length >= this.minChars)){
27944             if(this.lastQuery != q || this.alwaysQuery){
27945                 this.lastQuery = q;
27946                 if(this.mode == 'local'){
27947                     this.selectedIndex = -1;
27948                     if(forceAll){
27949                         this.store.clearFilter();
27950                     }else{
27951                         this.store.filter(this.displayField, q);
27952                     }
27953                     this.onLoad();
27954                 }else{
27955                     this.store.baseParams[this.queryParam] = q;
27956                     this.store.load({
27957                         params: this.getParams(q)
27958                     });
27959                     this.expand();
27960                 }
27961             }else{
27962                 this.selectedIndex = -1;
27963                 this.onLoad();   
27964             }
27965         }
27966     },
27967
27968     // private
27969     getParams : function(q){
27970         var p = {};
27971         //p[this.queryParam] = q;
27972         if(this.pageSize){
27973             p.start = 0;
27974             p.limit = this.pageSize;
27975         }
27976         return p;
27977     },
27978
27979     /**
27980      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
27981      */
27982     collapse : function(){
27983         
27984     },
27985
27986     // private
27987     collapseIf : function(e){
27988         
27989     },
27990
27991     /**
27992      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
27993      */
27994     expand : function(){
27995         
27996     } ,
27997
27998     // private
27999      
28000
28001     /** 
28002     * @cfg {Boolean} grow 
28003     * @hide 
28004     */
28005     /** 
28006     * @cfg {Number} growMin 
28007     * @hide 
28008     */
28009     /** 
28010     * @cfg {Number} growMax 
28011     * @hide 
28012     */
28013     /**
28014      * @hide
28015      * @method autoSize
28016      */
28017     
28018     setWidth : function()
28019     {
28020         
28021     },
28022     getResizeEl : function(){
28023         return this.el;
28024     }
28025 });//<script type="text/javasscript">
28026  
28027
28028 /**
28029  * @class Roo.DDView
28030  * A DnD enabled version of Roo.View.
28031  * @param {Element/String} container The Element in which to create the View.
28032  * @param {String} tpl The template string used to create the markup for each element of the View
28033  * @param {Object} config The configuration properties. These include all the config options of
28034  * {@link Roo.View} plus some specific to this class.<br>
28035  * <p>
28036  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
28037  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
28038  * <p>
28039  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
28040 .x-view-drag-insert-above {
28041         border-top:1px dotted #3366cc;
28042 }
28043 .x-view-drag-insert-below {
28044         border-bottom:1px dotted #3366cc;
28045 }
28046 </code></pre>
28047  * 
28048  */
28049  
28050 Roo.DDView = function(container, tpl, config) {
28051     Roo.DDView.superclass.constructor.apply(this, arguments);
28052     this.getEl().setStyle("outline", "0px none");
28053     this.getEl().unselectable();
28054     if (this.dragGroup) {
28055                 this.setDraggable(this.dragGroup.split(","));
28056     }
28057     if (this.dropGroup) {
28058                 this.setDroppable(this.dropGroup.split(","));
28059     }
28060     if (this.deletable) {
28061         this.setDeletable();
28062     }
28063     this.isDirtyFlag = false;
28064         this.addEvents({
28065                 "drop" : true
28066         });
28067 };
28068
28069 Roo.extend(Roo.DDView, Roo.View, {
28070 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
28071 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
28072 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
28073 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
28074
28075         isFormField: true,
28076
28077         reset: Roo.emptyFn,
28078         
28079         clearInvalid: Roo.form.Field.prototype.clearInvalid,
28080
28081         validate: function() {
28082                 return true;
28083         },
28084         
28085         destroy: function() {
28086                 this.purgeListeners();
28087                 this.getEl.removeAllListeners();
28088                 this.getEl().remove();
28089                 if (this.dragZone) {
28090                         if (this.dragZone.destroy) {
28091                                 this.dragZone.destroy();
28092                         }
28093                 }
28094                 if (this.dropZone) {
28095                         if (this.dropZone.destroy) {
28096                                 this.dropZone.destroy();
28097                         }
28098                 }
28099         },
28100
28101 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
28102         getName: function() {
28103                 return this.name;
28104         },
28105
28106 /**     Loads the View from a JSON string representing the Records to put into the Store. */
28107         setValue: function(v) {
28108                 if (!this.store) {
28109                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
28110                 }
28111                 var data = {};
28112                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
28113                 this.store.proxy = new Roo.data.MemoryProxy(data);
28114                 this.store.load();
28115         },
28116
28117 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
28118         getValue: function() {
28119                 var result = '(';
28120                 this.store.each(function(rec) {
28121                         result += rec.id + ',';
28122                 });
28123                 return result.substr(0, result.length - 1) + ')';
28124         },
28125         
28126         getIds: function() {
28127                 var i = 0, result = new Array(this.store.getCount());
28128                 this.store.each(function(rec) {
28129                         result[i++] = rec.id;
28130                 });
28131                 return result;
28132         },
28133         
28134         isDirty: function() {
28135                 return this.isDirtyFlag;
28136         },
28137
28138 /**
28139  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
28140  *      whole Element becomes the target, and this causes the drop gesture to append.
28141  */
28142     getTargetFromEvent : function(e) {
28143                 var target = e.getTarget();
28144                 while ((target !== null) && (target.parentNode != this.el.dom)) {
28145                 target = target.parentNode;
28146                 }
28147                 if (!target) {
28148                         target = this.el.dom.lastChild || this.el.dom;
28149                 }
28150                 return target;
28151     },
28152
28153 /**
28154  *      Create the drag data which consists of an object which has the property "ddel" as
28155  *      the drag proxy element. 
28156  */
28157     getDragData : function(e) {
28158         var target = this.findItemFromChild(e.getTarget());
28159                 if(target) {
28160                         this.handleSelection(e);
28161                         var selNodes = this.getSelectedNodes();
28162             var dragData = {
28163                 source: this,
28164                 copy: this.copy || (this.allowCopy && e.ctrlKey),
28165                 nodes: selNodes,
28166                 records: []
28167                         };
28168                         var selectedIndices = this.getSelectedIndexes();
28169                         for (var i = 0; i < selectedIndices.length; i++) {
28170                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
28171                         }
28172                         if (selNodes.length == 1) {
28173                                 dragData.ddel = target.cloneNode(true); // the div element
28174                         } else {
28175                                 var div = document.createElement('div'); // create the multi element drag "ghost"
28176                                 div.className = 'multi-proxy';
28177                                 for (var i = 0, len = selNodes.length; i < len; i++) {
28178                                         div.appendChild(selNodes[i].cloneNode(true));
28179                                 }
28180                                 dragData.ddel = div;
28181                         }
28182             //console.log(dragData)
28183             //console.log(dragData.ddel.innerHTML)
28184                         return dragData;
28185                 }
28186         //console.log('nodragData')
28187                 return false;
28188     },
28189     
28190 /**     Specify to which ddGroup items in this DDView may be dragged. */
28191     setDraggable: function(ddGroup) {
28192         if (ddGroup instanceof Array) {
28193                 Roo.each(ddGroup, this.setDraggable, this);
28194                 return;
28195         }
28196         if (this.dragZone) {
28197                 this.dragZone.addToGroup(ddGroup);
28198         } else {
28199                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
28200                                 containerScroll: true,
28201                                 ddGroup: ddGroup 
28202
28203                         });
28204 //                      Draggability implies selection. DragZone's mousedown selects the element.
28205                         if (!this.multiSelect) { this.singleSelect = true; }
28206
28207 //                      Wire the DragZone's handlers up to methods in *this*
28208                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
28209                 }
28210     },
28211
28212 /**     Specify from which ddGroup this DDView accepts drops. */
28213     setDroppable: function(ddGroup) {
28214         if (ddGroup instanceof Array) {
28215                 Roo.each(ddGroup, this.setDroppable, this);
28216                 return;
28217         }
28218         if (this.dropZone) {
28219                 this.dropZone.addToGroup(ddGroup);
28220         } else {
28221                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
28222                                 containerScroll: true,
28223                                 ddGroup: ddGroup
28224                         });
28225
28226 //                      Wire the DropZone's handlers up to methods in *this*
28227                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
28228                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
28229                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
28230                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
28231                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
28232                 }
28233     },
28234
28235 /**     Decide whether to drop above or below a View node. */
28236     getDropPoint : function(e, n, dd){
28237         if (n == this.el.dom) { return "above"; }
28238                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
28239                 var c = t + (b - t) / 2;
28240                 var y = Roo.lib.Event.getPageY(e);
28241                 if(y <= c) {
28242                         return "above";
28243                 }else{
28244                         return "below";
28245                 }
28246     },
28247
28248     onNodeEnter : function(n, dd, e, data){
28249                 return false;
28250     },
28251     
28252     onNodeOver : function(n, dd, e, data){
28253                 var pt = this.getDropPoint(e, n, dd);
28254                 // set the insert point style on the target node
28255                 var dragElClass = this.dropNotAllowed;
28256                 if (pt) {
28257                         var targetElClass;
28258                         if (pt == "above"){
28259                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
28260                                 targetElClass = "x-view-drag-insert-above";
28261                         } else {
28262                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
28263                                 targetElClass = "x-view-drag-insert-below";
28264                         }
28265                         if (this.lastInsertClass != targetElClass){
28266                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
28267                                 this.lastInsertClass = targetElClass;
28268                         }
28269                 }
28270                 return dragElClass;
28271         },
28272
28273     onNodeOut : function(n, dd, e, data){
28274                 this.removeDropIndicators(n);
28275     },
28276
28277     onNodeDrop : function(n, dd, e, data){
28278         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
28279                 return false;
28280         }
28281         var pt = this.getDropPoint(e, n, dd);
28282                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
28283                 if (pt == "below") { insertAt++; }
28284                 for (var i = 0; i < data.records.length; i++) {
28285                         var r = data.records[i];
28286                         var dup = this.store.getById(r.id);
28287                         if (dup && (dd != this.dragZone)) {
28288                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
28289                         } else {
28290                                 if (data.copy) {
28291                                         this.store.insert(insertAt++, r.copy());
28292                                 } else {
28293                                         data.source.isDirtyFlag = true;
28294                                         r.store.remove(r);
28295                                         this.store.insert(insertAt++, r);
28296                                 }
28297                                 this.isDirtyFlag = true;
28298                         }
28299                 }
28300                 this.dragZone.cachedTarget = null;
28301                 return true;
28302     },
28303
28304     removeDropIndicators : function(n){
28305                 if(n){
28306                         Roo.fly(n).removeClass([
28307                                 "x-view-drag-insert-above",
28308                                 "x-view-drag-insert-below"]);
28309                         this.lastInsertClass = "_noclass";
28310                 }
28311     },
28312
28313 /**
28314  *      Utility method. Add a delete option to the DDView's context menu.
28315  *      @param {String} imageUrl The URL of the "delete" icon image.
28316  */
28317         setDeletable: function(imageUrl) {
28318                 if (!this.singleSelect && !this.multiSelect) {
28319                         this.singleSelect = true;
28320                 }
28321                 var c = this.getContextMenu();
28322                 this.contextMenu.on("itemclick", function(item) {
28323                         switch (item.id) {
28324                                 case "delete":
28325                                         this.remove(this.getSelectedIndexes());
28326                                         break;
28327                         }
28328                 }, this);
28329                 this.contextMenu.add({
28330                         icon: imageUrl,
28331                         id: "delete",
28332                         text: 'Delete'
28333                 });
28334         },
28335         
28336 /**     Return the context menu for this DDView. */
28337         getContextMenu: function() {
28338                 if (!this.contextMenu) {
28339 //                      Create the View's context menu
28340                         this.contextMenu = new Roo.menu.Menu({
28341                                 id: this.id + "-contextmenu"
28342                         });
28343                         this.el.on("contextmenu", this.showContextMenu, this);
28344                 }
28345                 return this.contextMenu;
28346         },
28347         
28348         disableContextMenu: function() {
28349                 if (this.contextMenu) {
28350                         this.el.un("contextmenu", this.showContextMenu, this);
28351                 }
28352         },
28353
28354         showContextMenu: function(e, item) {
28355         item = this.findItemFromChild(e.getTarget());
28356                 if (item) {
28357                         e.stopEvent();
28358                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
28359                         this.contextMenu.showAt(e.getXY());
28360             }
28361     },
28362
28363 /**
28364  *      Remove {@link Roo.data.Record}s at the specified indices.
28365  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
28366  */
28367     remove: function(selectedIndices) {
28368                 selectedIndices = [].concat(selectedIndices);
28369                 for (var i = 0; i < selectedIndices.length; i++) {
28370                         var rec = this.store.getAt(selectedIndices[i]);
28371                         this.store.remove(rec);
28372                 }
28373     },
28374
28375 /**
28376  *      Double click fires the event, but also, if this is draggable, and there is only one other
28377  *      related DropZone, it transfers the selected node.
28378  */
28379     onDblClick : function(e){
28380         var item = this.findItemFromChild(e.getTarget());
28381         if(item){
28382             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
28383                 return false;
28384             }
28385             if (this.dragGroup) {
28386                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
28387                     while (targets.indexOf(this.dropZone) > -1) {
28388                             targets.remove(this.dropZone);
28389                                 }
28390                     if (targets.length == 1) {
28391                                         this.dragZone.cachedTarget = null;
28392                         var el = Roo.get(targets[0].getEl());
28393                         var box = el.getBox(true);
28394                         targets[0].onNodeDrop(el.dom, {
28395                                 target: el.dom,
28396                                 xy: [box.x, box.y + box.height - 1]
28397                         }, null, this.getDragData(e));
28398                     }
28399                 }
28400         }
28401     },
28402     
28403     handleSelection: function(e) {
28404                 this.dragZone.cachedTarget = null;
28405         var item = this.findItemFromChild(e.getTarget());
28406         if (!item) {
28407                 this.clearSelections(true);
28408                 return;
28409         }
28410                 if (item && (this.multiSelect || this.singleSelect)){
28411                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
28412                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
28413                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
28414                                 this.unselect(item);
28415                         } else {
28416                                 this.select(item, this.multiSelect && e.ctrlKey);
28417                                 this.lastSelection = item;
28418                         }
28419                 }
28420     },
28421
28422     onItemClick : function(item, index, e){
28423                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
28424                         return false;
28425                 }
28426                 return true;
28427     },
28428
28429     unselect : function(nodeInfo, suppressEvent){
28430                 var node = this.getNode(nodeInfo);
28431                 if(node && this.isSelected(node)){
28432                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28433                                 Roo.fly(node).removeClass(this.selectedClass);
28434                                 this.selections.remove(node);
28435                                 if(!suppressEvent){
28436                                         this.fireEvent("selectionchange", this, this.selections);
28437                                 }
28438                         }
28439                 }
28440     }
28441 });
28442 /*
28443  * Based on:
28444  * Ext JS Library 1.1.1
28445  * Copyright(c) 2006-2007, Ext JS, LLC.
28446  *
28447  * Originally Released Under LGPL - original licence link has changed is not relivant.
28448  *
28449  * Fork - LGPL
28450  * <script type="text/javascript">
28451  */
28452  
28453 /**
28454  * @class Roo.LayoutManager
28455  * @extends Roo.util.Observable
28456  * Base class for layout managers.
28457  */
28458 Roo.LayoutManager = function(container, config){
28459     Roo.LayoutManager.superclass.constructor.call(this);
28460     this.el = Roo.get(container);
28461     // ie scrollbar fix
28462     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
28463         document.body.scroll = "no";
28464     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
28465         this.el.position('relative');
28466     }
28467     this.id = this.el.id;
28468     this.el.addClass("x-layout-container");
28469     /** false to disable window resize monitoring @type Boolean */
28470     this.monitorWindowResize = true;
28471     this.regions = {};
28472     this.addEvents({
28473         /**
28474          * @event layout
28475          * Fires when a layout is performed. 
28476          * @param {Roo.LayoutManager} this
28477          */
28478         "layout" : true,
28479         /**
28480          * @event regionresized
28481          * Fires when the user resizes a region. 
28482          * @param {Roo.LayoutRegion} region The resized region
28483          * @param {Number} newSize The new size (width for east/west, height for north/south)
28484          */
28485         "regionresized" : true,
28486         /**
28487          * @event regioncollapsed
28488          * Fires when a region is collapsed. 
28489          * @param {Roo.LayoutRegion} region The collapsed region
28490          */
28491         "regioncollapsed" : true,
28492         /**
28493          * @event regionexpanded
28494          * Fires when a region is expanded.  
28495          * @param {Roo.LayoutRegion} region The expanded region
28496          */
28497         "regionexpanded" : true
28498     });
28499     this.updating = false;
28500     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
28501 };
28502
28503 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
28504     /**
28505      * Returns true if this layout is currently being updated
28506      * @return {Boolean}
28507      */
28508     isUpdating : function(){
28509         return this.updating; 
28510     },
28511     
28512     /**
28513      * Suspend the LayoutManager from doing auto-layouts while
28514      * making multiple add or remove calls
28515      */
28516     beginUpdate : function(){
28517         this.updating = true;    
28518     },
28519     
28520     /**
28521      * Restore auto-layouts and optionally disable the manager from performing a layout
28522      * @param {Boolean} noLayout true to disable a layout update 
28523      */
28524     endUpdate : function(noLayout){
28525         this.updating = false;
28526         if(!noLayout){
28527             this.layout();
28528         }    
28529     },
28530     
28531     layout: function(){
28532         
28533     },
28534     
28535     onRegionResized : function(region, newSize){
28536         this.fireEvent("regionresized", region, newSize);
28537         this.layout();
28538     },
28539     
28540     onRegionCollapsed : function(region){
28541         this.fireEvent("regioncollapsed", region);
28542     },
28543     
28544     onRegionExpanded : function(region){
28545         this.fireEvent("regionexpanded", region);
28546     },
28547         
28548     /**
28549      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
28550      * performs box-model adjustments.
28551      * @return {Object} The size as an object {width: (the width), height: (the height)}
28552      */
28553     getViewSize : function(){
28554         var size;
28555         if(this.el.dom != document.body){
28556             size = this.el.getSize();
28557         }else{
28558             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
28559         }
28560         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
28561         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
28562         return size;
28563     },
28564     
28565     /**
28566      * Returns the Element this layout is bound to.
28567      * @return {Roo.Element}
28568      */
28569     getEl : function(){
28570         return this.el;
28571     },
28572     
28573     /**
28574      * Returns the specified region.
28575      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
28576      * @return {Roo.LayoutRegion}
28577      */
28578     getRegion : function(target){
28579         return this.regions[target.toLowerCase()];
28580     },
28581     
28582     onWindowResize : function(){
28583         if(this.monitorWindowResize){
28584             this.layout();
28585         }
28586     }
28587 });/*
28588  * Based on:
28589  * Ext JS Library 1.1.1
28590  * Copyright(c) 2006-2007, Ext JS, LLC.
28591  *
28592  * Originally Released Under LGPL - original licence link has changed is not relivant.
28593  *
28594  * Fork - LGPL
28595  * <script type="text/javascript">
28596  */
28597 /**
28598  * @class Roo.BorderLayout
28599  * @extends Roo.LayoutManager
28600  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
28601  * please see: <br><br>
28602  * <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>
28603  * <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>
28604  * Example:
28605  <pre><code>
28606  var layout = new Roo.BorderLayout(document.body, {
28607     north: {
28608         initialSize: 25,
28609         titlebar: false
28610     },
28611     west: {
28612         split:true,
28613         initialSize: 200,
28614         minSize: 175,
28615         maxSize: 400,
28616         titlebar: true,
28617         collapsible: true
28618     },
28619     east: {
28620         split:true,
28621         initialSize: 202,
28622         minSize: 175,
28623         maxSize: 400,
28624         titlebar: true,
28625         collapsible: true
28626     },
28627     south: {
28628         split:true,
28629         initialSize: 100,
28630         minSize: 100,
28631         maxSize: 200,
28632         titlebar: true,
28633         collapsible: true
28634     },
28635     center: {
28636         titlebar: true,
28637         autoScroll:true,
28638         resizeTabs: true,
28639         minTabWidth: 50,
28640         preferredTabWidth: 150
28641     }
28642 });
28643
28644 // shorthand
28645 var CP = Roo.ContentPanel;
28646
28647 layout.beginUpdate();
28648 layout.add("north", new CP("north", "North"));
28649 layout.add("south", new CP("south", {title: "South", closable: true}));
28650 layout.add("west", new CP("west", {title: "West"}));
28651 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
28652 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
28653 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
28654 layout.getRegion("center").showPanel("center1");
28655 layout.endUpdate();
28656 </code></pre>
28657
28658 <b>The container the layout is rendered into can be either the body element or any other element.
28659 If it is not the body element, the container needs to either be an absolute positioned element,
28660 or you will need to add "position:relative" to the css of the container.  You will also need to specify
28661 the container size if it is not the body element.</b>
28662
28663 * @constructor
28664 * Create a new BorderLayout
28665 * @param {String/HTMLElement/Element} container The container this layout is bound to
28666 * @param {Object} config Configuration options
28667  */
28668 Roo.BorderLayout = function(container, config){
28669     config = config || {};
28670     Roo.BorderLayout.superclass.constructor.call(this, container, config);
28671     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
28672     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
28673         var target = this.factory.validRegions[i];
28674         if(config[target]){
28675             this.addRegion(target, config[target]);
28676         }
28677     }
28678 };
28679
28680 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
28681     /**
28682      * Creates and adds a new region if it doesn't already exist.
28683      * @param {String} target The target region key (north, south, east, west or center).
28684      * @param {Object} config The regions config object
28685      * @return {BorderLayoutRegion} The new region
28686      */
28687     addRegion : function(target, config){
28688         if(!this.regions[target]){
28689             var r = this.factory.create(target, this, config);
28690             this.bindRegion(target, r);
28691         }
28692         return this.regions[target];
28693     },
28694
28695     // private (kinda)
28696     bindRegion : function(name, r){
28697         this.regions[name] = r;
28698         r.on("visibilitychange", this.layout, this);
28699         r.on("paneladded", this.layout, this);
28700         r.on("panelremoved", this.layout, this);
28701         r.on("invalidated", this.layout, this);
28702         r.on("resized", this.onRegionResized, this);
28703         r.on("collapsed", this.onRegionCollapsed, this);
28704         r.on("expanded", this.onRegionExpanded, this);
28705     },
28706
28707     /**
28708      * Performs a layout update.
28709      */
28710     layout : function(){
28711         if(this.updating) {
28712             return;
28713         }
28714         var size = this.getViewSize();
28715         var w = size.width;
28716         var h = size.height;
28717         var centerW = w;
28718         var centerH = h;
28719         var centerY = 0;
28720         var centerX = 0;
28721         //var x = 0, y = 0;
28722
28723         var rs = this.regions;
28724         var north = rs["north"];
28725         var south = rs["south"]; 
28726         var west = rs["west"];
28727         var east = rs["east"];
28728         var center = rs["center"];
28729         //if(this.hideOnLayout){ // not supported anymore
28730             //c.el.setStyle("display", "none");
28731         //}
28732         if(north && north.isVisible()){
28733             var b = north.getBox();
28734             var m = north.getMargins();
28735             b.width = w - (m.left+m.right);
28736             b.x = m.left;
28737             b.y = m.top;
28738             centerY = b.height + b.y + m.bottom;
28739             centerH -= centerY;
28740             north.updateBox(this.safeBox(b));
28741         }
28742         if(south && south.isVisible()){
28743             var b = south.getBox();
28744             var m = south.getMargins();
28745             b.width = w - (m.left+m.right);
28746             b.x = m.left;
28747             var totalHeight = (b.height + m.top + m.bottom);
28748             b.y = h - totalHeight + m.top;
28749             centerH -= totalHeight;
28750             south.updateBox(this.safeBox(b));
28751         }
28752         if(west && west.isVisible()){
28753             var b = west.getBox();
28754             var m = west.getMargins();
28755             b.height = centerH - (m.top+m.bottom);
28756             b.x = m.left;
28757             b.y = centerY + m.top;
28758             var totalWidth = (b.width + m.left + m.right);
28759             centerX += totalWidth;
28760             centerW -= totalWidth;
28761             west.updateBox(this.safeBox(b));
28762         }
28763         if(east && east.isVisible()){
28764             var b = east.getBox();
28765             var m = east.getMargins();
28766             b.height = centerH - (m.top+m.bottom);
28767             var totalWidth = (b.width + m.left + m.right);
28768             b.x = w - totalWidth + m.left;
28769             b.y = centerY + m.top;
28770             centerW -= totalWidth;
28771             east.updateBox(this.safeBox(b));
28772         }
28773         if(center){
28774             var m = center.getMargins();
28775             var centerBox = {
28776                 x: centerX + m.left,
28777                 y: centerY + m.top,
28778                 width: centerW - (m.left+m.right),
28779                 height: centerH - (m.top+m.bottom)
28780             };
28781             //if(this.hideOnLayout){
28782                 //center.el.setStyle("display", "block");
28783             //}
28784             center.updateBox(this.safeBox(centerBox));
28785         }
28786         this.el.repaint();
28787         this.fireEvent("layout", this);
28788     },
28789
28790     // private
28791     safeBox : function(box){
28792         box.width = Math.max(0, box.width);
28793         box.height = Math.max(0, box.height);
28794         return box;
28795     },
28796
28797     /**
28798      * Adds a ContentPanel (or subclass) to this layout.
28799      * @param {String} target The target region key (north, south, east, west or center).
28800      * @param {Roo.ContentPanel} panel The panel to add
28801      * @return {Roo.ContentPanel} The added panel
28802      */
28803     add : function(target, panel){
28804          
28805         target = target.toLowerCase();
28806         return this.regions[target].add(panel);
28807     },
28808
28809     /**
28810      * Remove a ContentPanel (or subclass) to this layout.
28811      * @param {String} target The target region key (north, south, east, west or center).
28812      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
28813      * @return {Roo.ContentPanel} The removed panel
28814      */
28815     remove : function(target, panel){
28816         target = target.toLowerCase();
28817         return this.regions[target].remove(panel);
28818     },
28819
28820     /**
28821      * Searches all regions for a panel with the specified id
28822      * @param {String} panelId
28823      * @return {Roo.ContentPanel} The panel or null if it wasn't found
28824      */
28825     findPanel : function(panelId){
28826         var rs = this.regions;
28827         for(var target in rs){
28828             if(typeof rs[target] != "function"){
28829                 var p = rs[target].getPanel(panelId);
28830                 if(p){
28831                     return p;
28832                 }
28833             }
28834         }
28835         return null;
28836     },
28837
28838     /**
28839      * Searches all regions for a panel with the specified id and activates (shows) it.
28840      * @param {String/ContentPanel} panelId The panels id or the panel itself
28841      * @return {Roo.ContentPanel} The shown panel or null
28842      */
28843     showPanel : function(panelId) {
28844       var rs = this.regions;
28845       for(var target in rs){
28846          var r = rs[target];
28847          if(typeof r != "function"){
28848             if(r.hasPanel(panelId)){
28849                return r.showPanel(panelId);
28850             }
28851          }
28852       }
28853       return null;
28854    },
28855
28856    /**
28857      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
28858      * @param {Roo.state.Provider} provider (optional) An alternate state provider
28859      */
28860     restoreState : function(provider){
28861         if(!provider){
28862             provider = Roo.state.Manager;
28863         }
28864         var sm = new Roo.LayoutStateManager();
28865         sm.init(this, provider);
28866     },
28867
28868     /**
28869      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
28870      * object should contain properties for each region to add ContentPanels to, and each property's value should be
28871      * a valid ContentPanel config object.  Example:
28872      * <pre><code>
28873 // Create the main layout
28874 var layout = new Roo.BorderLayout('main-ct', {
28875     west: {
28876         split:true,
28877         minSize: 175,
28878         titlebar: true
28879     },
28880     center: {
28881         title:'Components'
28882     }
28883 }, 'main-ct');
28884
28885 // Create and add multiple ContentPanels at once via configs
28886 layout.batchAdd({
28887    west: {
28888        id: 'source-files',
28889        autoCreate:true,
28890        title:'Ext Source Files',
28891        autoScroll:true,
28892        fitToFrame:true
28893    },
28894    center : {
28895        el: cview,
28896        autoScroll:true,
28897        fitToFrame:true,
28898        toolbar: tb,
28899        resizeEl:'cbody'
28900    }
28901 });
28902 </code></pre>
28903      * @param {Object} regions An object containing ContentPanel configs by region name
28904      */
28905     batchAdd : function(regions){
28906         this.beginUpdate();
28907         for(var rname in regions){
28908             var lr = this.regions[rname];
28909             if(lr){
28910                 this.addTypedPanels(lr, regions[rname]);
28911             }
28912         }
28913         this.endUpdate();
28914     },
28915
28916     // private
28917     addTypedPanels : function(lr, ps){
28918         if(typeof ps == 'string'){
28919             lr.add(new Roo.ContentPanel(ps));
28920         }
28921         else if(ps instanceof Array){
28922             for(var i =0, len = ps.length; i < len; i++){
28923                 this.addTypedPanels(lr, ps[i]);
28924             }
28925         }
28926         else if(!ps.events){ // raw config?
28927             var el = ps.el;
28928             delete ps.el; // prevent conflict
28929             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
28930         }
28931         else {  // panel object assumed!
28932             lr.add(ps);
28933         }
28934     },
28935     /**
28936      * Adds a xtype elements to the layout.
28937      * <pre><code>
28938
28939 layout.addxtype({
28940        xtype : 'ContentPanel',
28941        region: 'west',
28942        items: [ .... ]
28943    }
28944 );
28945
28946 layout.addxtype({
28947         xtype : 'NestedLayoutPanel',
28948         region: 'west',
28949         layout: {
28950            center: { },
28951            west: { }   
28952         },
28953         items : [ ... list of content panels or nested layout panels.. ]
28954    }
28955 );
28956 </code></pre>
28957      * @param {Object} cfg Xtype definition of item to add.
28958      */
28959     addxtype : function(cfg)
28960     {
28961         // basically accepts a pannel...
28962         // can accept a layout region..!?!?
28963         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
28964         
28965         if (!cfg.xtype.match(/Panel$/)) {
28966             return false;
28967         }
28968         var ret = false;
28969         
28970         if (typeof(cfg.region) == 'undefined') {
28971             Roo.log("Failed to add Panel, region was not set");
28972             Roo.log(cfg);
28973             return false;
28974         }
28975         var region = cfg.region;
28976         delete cfg.region;
28977         
28978           
28979         var xitems = [];
28980         if (cfg.items) {
28981             xitems = cfg.items;
28982             delete cfg.items;
28983         }
28984         var nb = false;
28985         
28986         switch(cfg.xtype) 
28987         {
28988             case 'ContentPanel':  // ContentPanel (el, cfg)
28989             case 'ScrollPanel':  // ContentPanel (el, cfg)
28990             case 'ViewPanel': 
28991                 if(cfg.autoCreate) {
28992                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
28993                 } else {
28994                     var el = this.el.createChild();
28995                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
28996                 }
28997                 
28998                 this.add(region, ret);
28999                 break;
29000             
29001             
29002             case 'TreePanel': // our new panel!
29003                 cfg.el = this.el.createChild();
29004                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29005                 this.add(region, ret);
29006                 break;
29007             
29008             case 'NestedLayoutPanel': 
29009                 // create a new Layout (which is  a Border Layout...
29010                 var el = this.el.createChild();
29011                 var clayout = cfg.layout;
29012                 delete cfg.layout;
29013                 clayout.items   = clayout.items  || [];
29014                 // replace this exitems with the clayout ones..
29015                 xitems = clayout.items;
29016                  
29017                 
29018                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
29019                     cfg.background = false;
29020                 }
29021                 var layout = new Roo.BorderLayout(el, clayout);
29022                 
29023                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
29024                 //console.log('adding nested layout panel '  + cfg.toSource());
29025                 this.add(region, ret);
29026                 nb = {}; /// find first...
29027                 break;
29028                 
29029             case 'GridPanel': 
29030             
29031                 // needs grid and region
29032                 
29033                 //var el = this.getRegion(region).el.createChild();
29034                 var el = this.el.createChild();
29035                 // create the grid first...
29036                 
29037                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
29038                 delete cfg.grid;
29039                 if (region == 'center' && this.active ) {
29040                     cfg.background = false;
29041                 }
29042                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
29043                 
29044                 this.add(region, ret);
29045                 if (cfg.background) {
29046                     ret.on('activate', function(gp) {
29047                         if (!gp.grid.rendered) {
29048                             gp.grid.render();
29049                         }
29050                     });
29051                 } else {
29052                     grid.render();
29053                 }
29054                 break;
29055            
29056            
29057            
29058                 
29059                 
29060                 
29061             default:
29062                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
29063                     
29064                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29065                     this.add(region, ret);
29066                 } else {
29067                 
29068                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
29069                     return null;
29070                 }
29071                 
29072              // GridPanel (grid, cfg)
29073             
29074         }
29075         this.beginUpdate();
29076         // add children..
29077         var region = '';
29078         var abn = {};
29079         Roo.each(xitems, function(i)  {
29080             region = nb && i.region ? i.region : false;
29081             
29082             var add = ret.addxtype(i);
29083            
29084             if (region) {
29085                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
29086                 if (!i.background) {
29087                     abn[region] = nb[region] ;
29088                 }
29089             }
29090             
29091         });
29092         this.endUpdate();
29093
29094         // make the last non-background panel active..
29095         //if (nb) { Roo.log(abn); }
29096         if (nb) {
29097             
29098             for(var r in abn) {
29099                 region = this.getRegion(r);
29100                 if (region) {
29101                     // tried using nb[r], but it does not work..
29102                      
29103                     region.showPanel(abn[r]);
29104                    
29105                 }
29106             }
29107         }
29108         return ret;
29109         
29110     }
29111 });
29112
29113 /**
29114  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
29115  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
29116  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
29117  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
29118  * <pre><code>
29119 // shorthand
29120 var CP = Roo.ContentPanel;
29121
29122 var layout = Roo.BorderLayout.create({
29123     north: {
29124         initialSize: 25,
29125         titlebar: false,
29126         panels: [new CP("north", "North")]
29127     },
29128     west: {
29129         split:true,
29130         initialSize: 200,
29131         minSize: 175,
29132         maxSize: 400,
29133         titlebar: true,
29134         collapsible: true,
29135         panels: [new CP("west", {title: "West"})]
29136     },
29137     east: {
29138         split:true,
29139         initialSize: 202,
29140         minSize: 175,
29141         maxSize: 400,
29142         titlebar: true,
29143         collapsible: true,
29144         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
29145     },
29146     south: {
29147         split:true,
29148         initialSize: 100,
29149         minSize: 100,
29150         maxSize: 200,
29151         titlebar: true,
29152         collapsible: true,
29153         panels: [new CP("south", {title: "South", closable: true})]
29154     },
29155     center: {
29156         titlebar: true,
29157         autoScroll:true,
29158         resizeTabs: true,
29159         minTabWidth: 50,
29160         preferredTabWidth: 150,
29161         panels: [
29162             new CP("center1", {title: "Close Me", closable: true}),
29163             new CP("center2", {title: "Center Panel", closable: false})
29164         ]
29165     }
29166 }, document.body);
29167
29168 layout.getRegion("center").showPanel("center1");
29169 </code></pre>
29170  * @param config
29171  * @param targetEl
29172  */
29173 Roo.BorderLayout.create = function(config, targetEl){
29174     var layout = new Roo.BorderLayout(targetEl || document.body, config);
29175     layout.beginUpdate();
29176     var regions = Roo.BorderLayout.RegionFactory.validRegions;
29177     for(var j = 0, jlen = regions.length; j < jlen; j++){
29178         var lr = regions[j];
29179         if(layout.regions[lr] && config[lr].panels){
29180             var r = layout.regions[lr];
29181             var ps = config[lr].panels;
29182             layout.addTypedPanels(r, ps);
29183         }
29184     }
29185     layout.endUpdate();
29186     return layout;
29187 };
29188
29189 // private
29190 Roo.BorderLayout.RegionFactory = {
29191     // private
29192     validRegions : ["north","south","east","west","center"],
29193
29194     // private
29195     create : function(target, mgr, config){
29196         target = target.toLowerCase();
29197         if(config.lightweight || config.basic){
29198             return new Roo.BasicLayoutRegion(mgr, config, target);
29199         }
29200         switch(target){
29201             case "north":
29202                 return new Roo.NorthLayoutRegion(mgr, config);
29203             case "south":
29204                 return new Roo.SouthLayoutRegion(mgr, config);
29205             case "east":
29206                 return new Roo.EastLayoutRegion(mgr, config);
29207             case "west":
29208                 return new Roo.WestLayoutRegion(mgr, config);
29209             case "center":
29210                 return new Roo.CenterLayoutRegion(mgr, config);
29211         }
29212         throw 'Layout region "'+target+'" not supported.';
29213     }
29214 };/*
29215  * Based on:
29216  * Ext JS Library 1.1.1
29217  * Copyright(c) 2006-2007, Ext JS, LLC.
29218  *
29219  * Originally Released Under LGPL - original licence link has changed is not relivant.
29220  *
29221  * Fork - LGPL
29222  * <script type="text/javascript">
29223  */
29224  
29225 /**
29226  * @class Roo.BasicLayoutRegion
29227  * @extends Roo.util.Observable
29228  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
29229  * and does not have a titlebar, tabs or any other features. All it does is size and position 
29230  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
29231  */
29232 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
29233     this.mgr = mgr;
29234     this.position  = pos;
29235     this.events = {
29236         /**
29237          * @scope Roo.BasicLayoutRegion
29238          */
29239         
29240         /**
29241          * @event beforeremove
29242          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
29243          * @param {Roo.LayoutRegion} this
29244          * @param {Roo.ContentPanel} panel The panel
29245          * @param {Object} e The cancel event object
29246          */
29247         "beforeremove" : true,
29248         /**
29249          * @event invalidated
29250          * Fires when the layout for this region is changed.
29251          * @param {Roo.LayoutRegion} this
29252          */
29253         "invalidated" : true,
29254         /**
29255          * @event visibilitychange
29256          * Fires when this region is shown or hidden 
29257          * @param {Roo.LayoutRegion} this
29258          * @param {Boolean} visibility true or false
29259          */
29260         "visibilitychange" : true,
29261         /**
29262          * @event paneladded
29263          * Fires when a panel is added. 
29264          * @param {Roo.LayoutRegion} this
29265          * @param {Roo.ContentPanel} panel The panel
29266          */
29267         "paneladded" : true,
29268         /**
29269          * @event panelremoved
29270          * Fires when a panel is removed. 
29271          * @param {Roo.LayoutRegion} this
29272          * @param {Roo.ContentPanel} panel The panel
29273          */
29274         "panelremoved" : true,
29275         /**
29276          * @event beforecollapse
29277          * Fires when this region before collapse.
29278          * @param {Roo.LayoutRegion} this
29279          */
29280         "beforecollapse" : true,
29281         /**
29282          * @event collapsed
29283          * Fires when this region is collapsed.
29284          * @param {Roo.LayoutRegion} this
29285          */
29286         "collapsed" : true,
29287         /**
29288          * @event expanded
29289          * Fires when this region is expanded.
29290          * @param {Roo.LayoutRegion} this
29291          */
29292         "expanded" : true,
29293         /**
29294          * @event slideshow
29295          * Fires when this region is slid into view.
29296          * @param {Roo.LayoutRegion} this
29297          */
29298         "slideshow" : true,
29299         /**
29300          * @event slidehide
29301          * Fires when this region slides out of view. 
29302          * @param {Roo.LayoutRegion} this
29303          */
29304         "slidehide" : true,
29305         /**
29306          * @event panelactivated
29307          * Fires when a panel is activated. 
29308          * @param {Roo.LayoutRegion} this
29309          * @param {Roo.ContentPanel} panel The activated panel
29310          */
29311         "panelactivated" : true,
29312         /**
29313          * @event resized
29314          * Fires when the user resizes this region. 
29315          * @param {Roo.LayoutRegion} this
29316          * @param {Number} newSize The new size (width for east/west, height for north/south)
29317          */
29318         "resized" : true
29319     };
29320     /** A collection of panels in this region. @type Roo.util.MixedCollection */
29321     this.panels = new Roo.util.MixedCollection();
29322     this.panels.getKey = this.getPanelId.createDelegate(this);
29323     this.box = null;
29324     this.activePanel = null;
29325     // ensure listeners are added...
29326     
29327     if (config.listeners || config.events) {
29328         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
29329             listeners : config.listeners || {},
29330             events : config.events || {}
29331         });
29332     }
29333     
29334     if(skipConfig !== true){
29335         this.applyConfig(config);
29336     }
29337 };
29338
29339 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
29340     getPanelId : function(p){
29341         return p.getId();
29342     },
29343     
29344     applyConfig : function(config){
29345         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29346         this.config = config;
29347         
29348     },
29349     
29350     /**
29351      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
29352      * the width, for horizontal (north, south) the height.
29353      * @param {Number} newSize The new width or height
29354      */
29355     resizeTo : function(newSize){
29356         var el = this.el ? this.el :
29357                  (this.activePanel ? this.activePanel.getEl() : null);
29358         if(el){
29359             switch(this.position){
29360                 case "east":
29361                 case "west":
29362                     el.setWidth(newSize);
29363                     this.fireEvent("resized", this, newSize);
29364                 break;
29365                 case "north":
29366                 case "south":
29367                     el.setHeight(newSize);
29368                     this.fireEvent("resized", this, newSize);
29369                 break;                
29370             }
29371         }
29372     },
29373     
29374     getBox : function(){
29375         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
29376     },
29377     
29378     getMargins : function(){
29379         return this.margins;
29380     },
29381     
29382     updateBox : function(box){
29383         this.box = box;
29384         var el = this.activePanel.getEl();
29385         el.dom.style.left = box.x + "px";
29386         el.dom.style.top = box.y + "px";
29387         this.activePanel.setSize(box.width, box.height);
29388     },
29389     
29390     /**
29391      * Returns the container element for this region.
29392      * @return {Roo.Element}
29393      */
29394     getEl : function(){
29395         return this.activePanel;
29396     },
29397     
29398     /**
29399      * Returns true if this region is currently visible.
29400      * @return {Boolean}
29401      */
29402     isVisible : function(){
29403         return this.activePanel ? true : false;
29404     },
29405     
29406     setActivePanel : function(panel){
29407         panel = this.getPanel(panel);
29408         if(this.activePanel && this.activePanel != panel){
29409             this.activePanel.setActiveState(false);
29410             this.activePanel.getEl().setLeftTop(-10000,-10000);
29411         }
29412         this.activePanel = panel;
29413         panel.setActiveState(true);
29414         if(this.box){
29415             panel.setSize(this.box.width, this.box.height);
29416         }
29417         this.fireEvent("panelactivated", this, panel);
29418         this.fireEvent("invalidated");
29419     },
29420     
29421     /**
29422      * Show the specified panel.
29423      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
29424      * @return {Roo.ContentPanel} The shown panel or null
29425      */
29426     showPanel : function(panel){
29427         if(panel = this.getPanel(panel)){
29428             this.setActivePanel(panel);
29429         }
29430         return panel;
29431     },
29432     
29433     /**
29434      * Get the active panel for this region.
29435      * @return {Roo.ContentPanel} The active panel or null
29436      */
29437     getActivePanel : function(){
29438         return this.activePanel;
29439     },
29440     
29441     /**
29442      * Add the passed ContentPanel(s)
29443      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
29444      * @return {Roo.ContentPanel} The panel added (if only one was added)
29445      */
29446     add : function(panel){
29447         if(arguments.length > 1){
29448             for(var i = 0, len = arguments.length; i < len; i++) {
29449                 this.add(arguments[i]);
29450             }
29451             return null;
29452         }
29453         if(this.hasPanel(panel)){
29454             this.showPanel(panel);
29455             return panel;
29456         }
29457         var el = panel.getEl();
29458         if(el.dom.parentNode != this.mgr.el.dom){
29459             this.mgr.el.dom.appendChild(el.dom);
29460         }
29461         if(panel.setRegion){
29462             panel.setRegion(this);
29463         }
29464         this.panels.add(panel);
29465         el.setStyle("position", "absolute");
29466         if(!panel.background){
29467             this.setActivePanel(panel);
29468             if(this.config.initialSize && this.panels.getCount()==1){
29469                 this.resizeTo(this.config.initialSize);
29470             }
29471         }
29472         this.fireEvent("paneladded", this, panel);
29473         return panel;
29474     },
29475     
29476     /**
29477      * Returns true if the panel is in this region.
29478      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29479      * @return {Boolean}
29480      */
29481     hasPanel : function(panel){
29482         if(typeof panel == "object"){ // must be panel obj
29483             panel = panel.getId();
29484         }
29485         return this.getPanel(panel) ? true : false;
29486     },
29487     
29488     /**
29489      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
29490      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29491      * @param {Boolean} preservePanel Overrides the config preservePanel option
29492      * @return {Roo.ContentPanel} The panel that was removed
29493      */
29494     remove : function(panel, preservePanel){
29495         panel = this.getPanel(panel);
29496         if(!panel){
29497             return null;
29498         }
29499         var e = {};
29500         this.fireEvent("beforeremove", this, panel, e);
29501         if(e.cancel === true){
29502             return null;
29503         }
29504         var panelId = panel.getId();
29505         this.panels.removeKey(panelId);
29506         return panel;
29507     },
29508     
29509     /**
29510      * Returns the panel specified or null if it's not in this region.
29511      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29512      * @return {Roo.ContentPanel}
29513      */
29514     getPanel : function(id){
29515         if(typeof id == "object"){ // must be panel obj
29516             return id;
29517         }
29518         return this.panels.get(id);
29519     },
29520     
29521     /**
29522      * Returns this regions position (north/south/east/west/center).
29523      * @return {String} 
29524      */
29525     getPosition: function(){
29526         return this.position;    
29527     }
29528 });/*
29529  * Based on:
29530  * Ext JS Library 1.1.1
29531  * Copyright(c) 2006-2007, Ext JS, LLC.
29532  *
29533  * Originally Released Under LGPL - original licence link has changed is not relivant.
29534  *
29535  * Fork - LGPL
29536  * <script type="text/javascript">
29537  */
29538  
29539 /**
29540  * @class Roo.LayoutRegion
29541  * @extends Roo.BasicLayoutRegion
29542  * This class represents a region in a layout manager.
29543  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
29544  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
29545  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
29546  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
29547  * @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})
29548  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
29549  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
29550  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
29551  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
29552  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
29553  * @cfg {String}    title           The title for the region (overrides panel titles)
29554  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
29555  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
29556  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
29557  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
29558  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
29559  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
29560  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
29561  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
29562  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
29563  * @cfg {Boolean}   showPin         True to show a pin button
29564  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
29565  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
29566  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
29567  * @cfg {Number}    width           For East/West panels
29568  * @cfg {Number}    height          For North/South panels
29569  * @cfg {Boolean}   split           To show the splitter
29570  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
29571  */
29572 Roo.LayoutRegion = function(mgr, config, pos){
29573     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
29574     var dh = Roo.DomHelper;
29575     /** This region's container element 
29576     * @type Roo.Element */
29577     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
29578     /** This region's title element 
29579     * @type Roo.Element */
29580
29581     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
29582         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
29583         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
29584     ]}, true);
29585     this.titleEl.enableDisplayMode();
29586     /** This region's title text element 
29587     * @type HTMLElement */
29588     this.titleTextEl = this.titleEl.dom.firstChild;
29589     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
29590     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
29591     this.closeBtn.enableDisplayMode();
29592     this.closeBtn.on("click", this.closeClicked, this);
29593     this.closeBtn.hide();
29594
29595     this.createBody(config);
29596     this.visible = true;
29597     this.collapsed = false;
29598
29599     if(config.hideWhenEmpty){
29600         this.hide();
29601         this.on("paneladded", this.validateVisibility, this);
29602         this.on("panelremoved", this.validateVisibility, this);
29603     }
29604     this.applyConfig(config);
29605 };
29606
29607 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
29608
29609     createBody : function(){
29610         /** This region's body element 
29611         * @type Roo.Element */
29612         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
29613     },
29614
29615     applyConfig : function(c){
29616         if(c.collapsible && this.position != "center" && !this.collapsedEl){
29617             var dh = Roo.DomHelper;
29618             if(c.titlebar !== false){
29619                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
29620                 this.collapseBtn.on("click", this.collapse, this);
29621                 this.collapseBtn.enableDisplayMode();
29622
29623                 if(c.showPin === true || this.showPin){
29624                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
29625                     this.stickBtn.enableDisplayMode();
29626                     this.stickBtn.on("click", this.expand, this);
29627                     this.stickBtn.hide();
29628                 }
29629             }
29630             /** This region's collapsed element
29631             * @type Roo.Element */
29632             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
29633                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
29634             ]}, true);
29635             if(c.floatable !== false){
29636                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
29637                this.collapsedEl.on("click", this.collapseClick, this);
29638             }
29639
29640             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
29641                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
29642                    id: "message", unselectable: "on", style:{"float":"left"}});
29643                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
29644              }
29645             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
29646             this.expandBtn.on("click", this.expand, this);
29647         }
29648         if(this.collapseBtn){
29649             this.collapseBtn.setVisible(c.collapsible == true);
29650         }
29651         this.cmargins = c.cmargins || this.cmargins ||
29652                          (this.position == "west" || this.position == "east" ?
29653                              {top: 0, left: 2, right:2, bottom: 0} :
29654                              {top: 2, left: 0, right:0, bottom: 2});
29655         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29656         this.bottomTabs = c.tabPosition != "top";
29657         this.autoScroll = c.autoScroll || false;
29658         if(this.autoScroll){
29659             this.bodyEl.setStyle("overflow", "auto");
29660         }else{
29661             this.bodyEl.setStyle("overflow", "hidden");
29662         }
29663         //if(c.titlebar !== false){
29664             if((!c.titlebar && !c.title) || c.titlebar === false){
29665                 this.titleEl.hide();
29666             }else{
29667                 this.titleEl.show();
29668                 if(c.title){
29669                     this.titleTextEl.innerHTML = c.title;
29670                 }
29671             }
29672         //}
29673         this.duration = c.duration || .30;
29674         this.slideDuration = c.slideDuration || .45;
29675         this.config = c;
29676         if(c.collapsed){
29677             this.collapse(true);
29678         }
29679         if(c.hidden){
29680             this.hide();
29681         }
29682     },
29683     /**
29684      * Returns true if this region is currently visible.
29685      * @return {Boolean}
29686      */
29687     isVisible : function(){
29688         return this.visible;
29689     },
29690
29691     /**
29692      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
29693      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
29694      */
29695     setCollapsedTitle : function(title){
29696         title = title || "&#160;";
29697         if(this.collapsedTitleTextEl){
29698             this.collapsedTitleTextEl.innerHTML = title;
29699         }
29700     },
29701
29702     getBox : function(){
29703         var b;
29704         if(!this.collapsed){
29705             b = this.el.getBox(false, true);
29706         }else{
29707             b = this.collapsedEl.getBox(false, true);
29708         }
29709         return b;
29710     },
29711
29712     getMargins : function(){
29713         return this.collapsed ? this.cmargins : this.margins;
29714     },
29715
29716     highlight : function(){
29717         this.el.addClass("x-layout-panel-dragover");
29718     },
29719
29720     unhighlight : function(){
29721         this.el.removeClass("x-layout-panel-dragover");
29722     },
29723
29724     updateBox : function(box){
29725         this.box = box;
29726         if(!this.collapsed){
29727             this.el.dom.style.left = box.x + "px";
29728             this.el.dom.style.top = box.y + "px";
29729             this.updateBody(box.width, box.height);
29730         }else{
29731             this.collapsedEl.dom.style.left = box.x + "px";
29732             this.collapsedEl.dom.style.top = box.y + "px";
29733             this.collapsedEl.setSize(box.width, box.height);
29734         }
29735         if(this.tabs){
29736             this.tabs.autoSizeTabs();
29737         }
29738     },
29739
29740     updateBody : function(w, h){
29741         if(w !== null){
29742             this.el.setWidth(w);
29743             w -= this.el.getBorderWidth("rl");
29744             if(this.config.adjustments){
29745                 w += this.config.adjustments[0];
29746             }
29747         }
29748         if(h !== null){
29749             this.el.setHeight(h);
29750             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
29751             h -= this.el.getBorderWidth("tb");
29752             if(this.config.adjustments){
29753                 h += this.config.adjustments[1];
29754             }
29755             this.bodyEl.setHeight(h);
29756             if(this.tabs){
29757                 h = this.tabs.syncHeight(h);
29758             }
29759         }
29760         if(this.panelSize){
29761             w = w !== null ? w : this.panelSize.width;
29762             h = h !== null ? h : this.panelSize.height;
29763         }
29764         if(this.activePanel){
29765             var el = this.activePanel.getEl();
29766             w = w !== null ? w : el.getWidth();
29767             h = h !== null ? h : el.getHeight();
29768             this.panelSize = {width: w, height: h};
29769             this.activePanel.setSize(w, h);
29770         }
29771         if(Roo.isIE && this.tabs){
29772             this.tabs.el.repaint();
29773         }
29774     },
29775
29776     /**
29777      * Returns the container element for this region.
29778      * @return {Roo.Element}
29779      */
29780     getEl : function(){
29781         return this.el;
29782     },
29783
29784     /**
29785      * Hides this region.
29786      */
29787     hide : function(){
29788         if(!this.collapsed){
29789             this.el.dom.style.left = "-2000px";
29790             this.el.hide();
29791         }else{
29792             this.collapsedEl.dom.style.left = "-2000px";
29793             this.collapsedEl.hide();
29794         }
29795         this.visible = false;
29796         this.fireEvent("visibilitychange", this, false);
29797     },
29798
29799     /**
29800      * Shows this region if it was previously hidden.
29801      */
29802     show : function(){
29803         if(!this.collapsed){
29804             this.el.show();
29805         }else{
29806             this.collapsedEl.show();
29807         }
29808         this.visible = true;
29809         this.fireEvent("visibilitychange", this, true);
29810     },
29811
29812     closeClicked : function(){
29813         if(this.activePanel){
29814             this.remove(this.activePanel);
29815         }
29816     },
29817
29818     collapseClick : function(e){
29819         if(this.isSlid){
29820            e.stopPropagation();
29821            this.slideIn();
29822         }else{
29823            e.stopPropagation();
29824            this.slideOut();
29825         }
29826     },
29827
29828     /**
29829      * Collapses this region.
29830      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
29831      */
29832     collapse : function(skipAnim, skipCheck = false){
29833         if(this.collapsed) {
29834             return;
29835         }
29836         
29837         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
29838             
29839             this.collapsed = true;
29840             if(this.split){
29841                 this.split.el.hide();
29842             }
29843             if(this.config.animate && skipAnim !== true){
29844                 this.fireEvent("invalidated", this);
29845                 this.animateCollapse();
29846             }else{
29847                 this.el.setLocation(-20000,-20000);
29848                 this.el.hide();
29849                 this.collapsedEl.show();
29850                 this.fireEvent("collapsed", this);
29851                 this.fireEvent("invalidated", this);
29852             }
29853         }
29854         
29855     },
29856
29857     animateCollapse : function(){
29858         // overridden
29859     },
29860
29861     /**
29862      * Expands this region if it was previously collapsed.
29863      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
29864      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
29865      */
29866     expand : function(e, skipAnim){
29867         if(e) {
29868             e.stopPropagation();
29869         }
29870         if(!this.collapsed || this.el.hasActiveFx()) {
29871             return;
29872         }
29873         if(this.isSlid){
29874             this.afterSlideIn();
29875             skipAnim = true;
29876         }
29877         this.collapsed = false;
29878         if(this.config.animate && skipAnim !== true){
29879             this.animateExpand();
29880         }else{
29881             this.el.show();
29882             if(this.split){
29883                 this.split.el.show();
29884             }
29885             this.collapsedEl.setLocation(-2000,-2000);
29886             this.collapsedEl.hide();
29887             this.fireEvent("invalidated", this);
29888             this.fireEvent("expanded", this);
29889         }
29890     },
29891
29892     animateExpand : function(){
29893         // overridden
29894     },
29895
29896     initTabs : function()
29897     {
29898         this.bodyEl.setStyle("overflow", "hidden");
29899         var ts = new Roo.TabPanel(
29900                 this.bodyEl.dom,
29901                 {
29902                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
29903                     disableTooltips: this.config.disableTabTips,
29904                     toolbar : this.config.toolbar
29905                 }
29906         );
29907         if(this.config.hideTabs){
29908             ts.stripWrap.setDisplayed(false);
29909         }
29910         this.tabs = ts;
29911         ts.resizeTabs = this.config.resizeTabs === true;
29912         ts.minTabWidth = this.config.minTabWidth || 40;
29913         ts.maxTabWidth = this.config.maxTabWidth || 250;
29914         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
29915         ts.monitorResize = false;
29916         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
29917         ts.bodyEl.addClass('x-layout-tabs-body');
29918         this.panels.each(this.initPanelAsTab, this);
29919     },
29920
29921     initPanelAsTab : function(panel){
29922         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
29923                     this.config.closeOnTab && panel.isClosable());
29924         if(panel.tabTip !== undefined){
29925             ti.setTooltip(panel.tabTip);
29926         }
29927         ti.on("activate", function(){
29928               this.setActivePanel(panel);
29929         }, this);
29930         if(this.config.closeOnTab){
29931             ti.on("beforeclose", function(t, e){
29932                 e.cancel = true;
29933                 this.remove(panel);
29934             }, this);
29935         }
29936         return ti;
29937     },
29938
29939     updatePanelTitle : function(panel, title){
29940         if(this.activePanel == panel){
29941             this.updateTitle(title);
29942         }
29943         if(this.tabs){
29944             var ti = this.tabs.getTab(panel.getEl().id);
29945             ti.setText(title);
29946             if(panel.tabTip !== undefined){
29947                 ti.setTooltip(panel.tabTip);
29948             }
29949         }
29950     },
29951
29952     updateTitle : function(title){
29953         if(this.titleTextEl && !this.config.title){
29954             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
29955         }
29956     },
29957
29958     setActivePanel : function(panel){
29959         panel = this.getPanel(panel);
29960         if(this.activePanel && this.activePanel != panel){
29961             this.activePanel.setActiveState(false);
29962         }
29963         this.activePanel = panel;
29964         panel.setActiveState(true);
29965         if(this.panelSize){
29966             panel.setSize(this.panelSize.width, this.panelSize.height);
29967         }
29968         if(this.closeBtn){
29969             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
29970         }
29971         this.updateTitle(panel.getTitle());
29972         if(this.tabs){
29973             this.fireEvent("invalidated", this);
29974         }
29975         this.fireEvent("panelactivated", this, panel);
29976     },
29977
29978     /**
29979      * Shows the specified panel.
29980      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
29981      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
29982      */
29983     showPanel : function(panel)
29984     {
29985         panel = this.getPanel(panel);
29986         if(panel){
29987             if(this.tabs){
29988                 var tab = this.tabs.getTab(panel.getEl().id);
29989                 if(tab.isHidden()){
29990                     this.tabs.unhideTab(tab.id);
29991                 }
29992                 tab.activate();
29993             }else{
29994                 this.setActivePanel(panel);
29995             }
29996         }
29997         return panel;
29998     },
29999
30000     /**
30001      * Get the active panel for this region.
30002      * @return {Roo.ContentPanel} The active panel or null
30003      */
30004     getActivePanel : function(){
30005         return this.activePanel;
30006     },
30007
30008     validateVisibility : function(){
30009         if(this.panels.getCount() < 1){
30010             this.updateTitle("&#160;");
30011             this.closeBtn.hide();
30012             this.hide();
30013         }else{
30014             if(!this.isVisible()){
30015                 this.show();
30016             }
30017         }
30018     },
30019
30020     /**
30021      * Adds the passed ContentPanel(s) to this region.
30022      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
30023      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
30024      */
30025     add : function(panel){
30026         if(arguments.length > 1){
30027             for(var i = 0, len = arguments.length; i < len; i++) {
30028                 this.add(arguments[i]);
30029             }
30030             return null;
30031         }
30032         if(this.hasPanel(panel)){
30033             this.showPanel(panel);
30034             return panel;
30035         }
30036         panel.setRegion(this);
30037         this.panels.add(panel);
30038         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
30039             this.bodyEl.dom.appendChild(panel.getEl().dom);
30040             if(panel.background !== true){
30041                 this.setActivePanel(panel);
30042             }
30043             this.fireEvent("paneladded", this, panel);
30044             return panel;
30045         }
30046         if(!this.tabs){
30047             this.initTabs();
30048         }else{
30049             this.initPanelAsTab(panel);
30050         }
30051         if(panel.background !== true){
30052             this.tabs.activate(panel.getEl().id);
30053         }
30054         this.fireEvent("paneladded", this, panel);
30055         return panel;
30056     },
30057
30058     /**
30059      * Hides the tab for the specified panel.
30060      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30061      */
30062     hidePanel : function(panel){
30063         if(this.tabs && (panel = this.getPanel(panel))){
30064             this.tabs.hideTab(panel.getEl().id);
30065         }
30066     },
30067
30068     /**
30069      * Unhides the tab for a previously hidden panel.
30070      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30071      */
30072     unhidePanel : function(panel){
30073         if(this.tabs && (panel = this.getPanel(panel))){
30074             this.tabs.unhideTab(panel.getEl().id);
30075         }
30076     },
30077
30078     clearPanels : function(){
30079         while(this.panels.getCount() > 0){
30080              this.remove(this.panels.first());
30081         }
30082     },
30083
30084     /**
30085      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30086      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30087      * @param {Boolean} preservePanel Overrides the config preservePanel option
30088      * @return {Roo.ContentPanel} The panel that was removed
30089      */
30090     remove : function(panel, preservePanel){
30091         panel = this.getPanel(panel);
30092         if(!panel){
30093             return null;
30094         }
30095         var e = {};
30096         this.fireEvent("beforeremove", this, panel, e);
30097         if(e.cancel === true){
30098             return null;
30099         }
30100         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
30101         var panelId = panel.getId();
30102         this.panels.removeKey(panelId);
30103         if(preservePanel){
30104             document.body.appendChild(panel.getEl().dom);
30105         }
30106         if(this.tabs){
30107             this.tabs.removeTab(panel.getEl().id);
30108         }else if (!preservePanel){
30109             this.bodyEl.dom.removeChild(panel.getEl().dom);
30110         }
30111         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
30112             var p = this.panels.first();
30113             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
30114             tempEl.appendChild(p.getEl().dom);
30115             this.bodyEl.update("");
30116             this.bodyEl.dom.appendChild(p.getEl().dom);
30117             tempEl = null;
30118             this.updateTitle(p.getTitle());
30119             this.tabs = null;
30120             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30121             this.setActivePanel(p);
30122         }
30123         panel.setRegion(null);
30124         if(this.activePanel == panel){
30125             this.activePanel = null;
30126         }
30127         if(this.config.autoDestroy !== false && preservePanel !== true){
30128             try{panel.destroy();}catch(e){}
30129         }
30130         this.fireEvent("panelremoved", this, panel);
30131         return panel;
30132     },
30133
30134     /**
30135      * Returns the TabPanel component used by this region
30136      * @return {Roo.TabPanel}
30137      */
30138     getTabs : function(){
30139         return this.tabs;
30140     },
30141
30142     createTool : function(parentEl, className){
30143         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
30144             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
30145         btn.addClassOnOver("x-layout-tools-button-over");
30146         return btn;
30147     }
30148 });/*
30149  * Based on:
30150  * Ext JS Library 1.1.1
30151  * Copyright(c) 2006-2007, Ext JS, LLC.
30152  *
30153  * Originally Released Under LGPL - original licence link has changed is not relivant.
30154  *
30155  * Fork - LGPL
30156  * <script type="text/javascript">
30157  */
30158  
30159
30160
30161 /**
30162  * @class Roo.SplitLayoutRegion
30163  * @extends Roo.LayoutRegion
30164  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
30165  */
30166 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
30167     this.cursor = cursor;
30168     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
30169 };
30170
30171 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
30172     splitTip : "Drag to resize.",
30173     collapsibleSplitTip : "Drag to resize. Double click to hide.",
30174     useSplitTips : false,
30175
30176     applyConfig : function(config){
30177         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
30178         if(config.split){
30179             if(!this.split){
30180                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
30181                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
30182                 /** The SplitBar for this region 
30183                 * @type Roo.SplitBar */
30184                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
30185                 this.split.on("moved", this.onSplitMove, this);
30186                 this.split.useShim = config.useShim === true;
30187                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
30188                 if(this.useSplitTips){
30189                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
30190                 }
30191                 if(config.collapsible){
30192                     this.split.el.on("dblclick", this.collapse,  this);
30193                 }
30194             }
30195             if(typeof config.minSize != "undefined"){
30196                 this.split.minSize = config.minSize;
30197             }
30198             if(typeof config.maxSize != "undefined"){
30199                 this.split.maxSize = config.maxSize;
30200             }
30201             if(config.hideWhenEmpty || config.hidden || config.collapsed){
30202                 this.hideSplitter();
30203             }
30204         }
30205     },
30206
30207     getHMaxSize : function(){
30208          var cmax = this.config.maxSize || 10000;
30209          var center = this.mgr.getRegion("center");
30210          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
30211     },
30212
30213     getVMaxSize : function(){
30214          var cmax = this.config.maxSize || 10000;
30215          var center = this.mgr.getRegion("center");
30216          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
30217     },
30218
30219     onSplitMove : function(split, newSize){
30220         this.fireEvent("resized", this, newSize);
30221     },
30222     
30223     /** 
30224      * Returns the {@link Roo.SplitBar} for this region.
30225      * @return {Roo.SplitBar}
30226      */
30227     getSplitBar : function(){
30228         return this.split;
30229     },
30230     
30231     hide : function(){
30232         this.hideSplitter();
30233         Roo.SplitLayoutRegion.superclass.hide.call(this);
30234     },
30235
30236     hideSplitter : function(){
30237         if(this.split){
30238             this.split.el.setLocation(-2000,-2000);
30239             this.split.el.hide();
30240         }
30241     },
30242
30243     show : function(){
30244         if(this.split){
30245             this.split.el.show();
30246         }
30247         Roo.SplitLayoutRegion.superclass.show.call(this);
30248     },
30249     
30250     beforeSlide: function(){
30251         if(Roo.isGecko){// firefox overflow auto bug workaround
30252             this.bodyEl.clip();
30253             if(this.tabs) {
30254                 this.tabs.bodyEl.clip();
30255             }
30256             if(this.activePanel){
30257                 this.activePanel.getEl().clip();
30258                 
30259                 if(this.activePanel.beforeSlide){
30260                     this.activePanel.beforeSlide();
30261                 }
30262             }
30263         }
30264     },
30265     
30266     afterSlide : function(){
30267         if(Roo.isGecko){// firefox overflow auto bug workaround
30268             this.bodyEl.unclip();
30269             if(this.tabs) {
30270                 this.tabs.bodyEl.unclip();
30271             }
30272             if(this.activePanel){
30273                 this.activePanel.getEl().unclip();
30274                 if(this.activePanel.afterSlide){
30275                     this.activePanel.afterSlide();
30276                 }
30277             }
30278         }
30279     },
30280
30281     initAutoHide : function(){
30282         if(this.autoHide !== false){
30283             if(!this.autoHideHd){
30284                 var st = new Roo.util.DelayedTask(this.slideIn, this);
30285                 this.autoHideHd = {
30286                     "mouseout": function(e){
30287                         if(!e.within(this.el, true)){
30288                             st.delay(500);
30289                         }
30290                     },
30291                     "mouseover" : function(e){
30292                         st.cancel();
30293                     },
30294                     scope : this
30295                 };
30296             }
30297             this.el.on(this.autoHideHd);
30298         }
30299     },
30300
30301     clearAutoHide : function(){
30302         if(this.autoHide !== false){
30303             this.el.un("mouseout", this.autoHideHd.mouseout);
30304             this.el.un("mouseover", this.autoHideHd.mouseover);
30305         }
30306     },
30307
30308     clearMonitor : function(){
30309         Roo.get(document).un("click", this.slideInIf, this);
30310     },
30311
30312     // these names are backwards but not changed for compat
30313     slideOut : function(){
30314         if(this.isSlid || this.el.hasActiveFx()){
30315             return;
30316         }
30317         this.isSlid = true;
30318         if(this.collapseBtn){
30319             this.collapseBtn.hide();
30320         }
30321         this.closeBtnState = this.closeBtn.getStyle('display');
30322         this.closeBtn.hide();
30323         if(this.stickBtn){
30324             this.stickBtn.show();
30325         }
30326         this.el.show();
30327         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
30328         this.beforeSlide();
30329         this.el.setStyle("z-index", 10001);
30330         this.el.slideIn(this.getSlideAnchor(), {
30331             callback: function(){
30332                 this.afterSlide();
30333                 this.initAutoHide();
30334                 Roo.get(document).on("click", this.slideInIf, this);
30335                 this.fireEvent("slideshow", this);
30336             },
30337             scope: this,
30338             block: true
30339         });
30340     },
30341
30342     afterSlideIn : function(){
30343         this.clearAutoHide();
30344         this.isSlid = false;
30345         this.clearMonitor();
30346         this.el.setStyle("z-index", "");
30347         if(this.collapseBtn){
30348             this.collapseBtn.show();
30349         }
30350         this.closeBtn.setStyle('display', this.closeBtnState);
30351         if(this.stickBtn){
30352             this.stickBtn.hide();
30353         }
30354         this.fireEvent("slidehide", this);
30355     },
30356
30357     slideIn : function(cb){
30358         if(!this.isSlid || this.el.hasActiveFx()){
30359             Roo.callback(cb);
30360             return;
30361         }
30362         this.isSlid = false;
30363         this.beforeSlide();
30364         this.el.slideOut(this.getSlideAnchor(), {
30365             callback: function(){
30366                 this.el.setLeftTop(-10000, -10000);
30367                 this.afterSlide();
30368                 this.afterSlideIn();
30369                 Roo.callback(cb);
30370             },
30371             scope: this,
30372             block: true
30373         });
30374     },
30375     
30376     slideInIf : function(e){
30377         if(!e.within(this.el)){
30378             this.slideIn();
30379         }
30380     },
30381
30382     animateCollapse : function(){
30383         this.beforeSlide();
30384         this.el.setStyle("z-index", 20000);
30385         var anchor = this.getSlideAnchor();
30386         this.el.slideOut(anchor, {
30387             callback : function(){
30388                 this.el.setStyle("z-index", "");
30389                 this.collapsedEl.slideIn(anchor, {duration:.3});
30390                 this.afterSlide();
30391                 this.el.setLocation(-10000,-10000);
30392                 this.el.hide();
30393                 this.fireEvent("collapsed", this);
30394             },
30395             scope: this,
30396             block: true
30397         });
30398     },
30399
30400     animateExpand : function(){
30401         this.beforeSlide();
30402         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
30403         this.el.setStyle("z-index", 20000);
30404         this.collapsedEl.hide({
30405             duration:.1
30406         });
30407         this.el.slideIn(this.getSlideAnchor(), {
30408             callback : function(){
30409                 this.el.setStyle("z-index", "");
30410                 this.afterSlide();
30411                 if(this.split){
30412                     this.split.el.show();
30413                 }
30414                 this.fireEvent("invalidated", this);
30415                 this.fireEvent("expanded", this);
30416             },
30417             scope: this,
30418             block: true
30419         });
30420     },
30421
30422     anchors : {
30423         "west" : "left",
30424         "east" : "right",
30425         "north" : "top",
30426         "south" : "bottom"
30427     },
30428
30429     sanchors : {
30430         "west" : "l",
30431         "east" : "r",
30432         "north" : "t",
30433         "south" : "b"
30434     },
30435
30436     canchors : {
30437         "west" : "tl-tr",
30438         "east" : "tr-tl",
30439         "north" : "tl-bl",
30440         "south" : "bl-tl"
30441     },
30442
30443     getAnchor : function(){
30444         return this.anchors[this.position];
30445     },
30446
30447     getCollapseAnchor : function(){
30448         return this.canchors[this.position];
30449     },
30450
30451     getSlideAnchor : function(){
30452         return this.sanchors[this.position];
30453     },
30454
30455     getAlignAdj : function(){
30456         var cm = this.cmargins;
30457         switch(this.position){
30458             case "west":
30459                 return [0, 0];
30460             break;
30461             case "east":
30462                 return [0, 0];
30463             break;
30464             case "north":
30465                 return [0, 0];
30466             break;
30467             case "south":
30468                 return [0, 0];
30469             break;
30470         }
30471     },
30472
30473     getExpandAdj : function(){
30474         var c = this.collapsedEl, cm = this.cmargins;
30475         switch(this.position){
30476             case "west":
30477                 return [-(cm.right+c.getWidth()+cm.left), 0];
30478             break;
30479             case "east":
30480                 return [cm.right+c.getWidth()+cm.left, 0];
30481             break;
30482             case "north":
30483                 return [0, -(cm.top+cm.bottom+c.getHeight())];
30484             break;
30485             case "south":
30486                 return [0, cm.top+cm.bottom+c.getHeight()];
30487             break;
30488         }
30489     }
30490 });/*
30491  * Based on:
30492  * Ext JS Library 1.1.1
30493  * Copyright(c) 2006-2007, Ext JS, LLC.
30494  *
30495  * Originally Released Under LGPL - original licence link has changed is not relivant.
30496  *
30497  * Fork - LGPL
30498  * <script type="text/javascript">
30499  */
30500 /*
30501  * These classes are private internal classes
30502  */
30503 Roo.CenterLayoutRegion = function(mgr, config){
30504     Roo.LayoutRegion.call(this, mgr, config, "center");
30505     this.visible = true;
30506     this.minWidth = config.minWidth || 20;
30507     this.minHeight = config.minHeight || 20;
30508 };
30509
30510 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
30511     hide : function(){
30512         // center panel can't be hidden
30513     },
30514     
30515     show : function(){
30516         // center panel can't be hidden
30517     },
30518     
30519     getMinWidth: function(){
30520         return this.minWidth;
30521     },
30522     
30523     getMinHeight: function(){
30524         return this.minHeight;
30525     }
30526 });
30527
30528
30529 Roo.NorthLayoutRegion = function(mgr, config){
30530     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
30531     if(this.split){
30532         this.split.placement = Roo.SplitBar.TOP;
30533         this.split.orientation = Roo.SplitBar.VERTICAL;
30534         this.split.el.addClass("x-layout-split-v");
30535     }
30536     var size = config.initialSize || config.height;
30537     if(typeof size != "undefined"){
30538         this.el.setHeight(size);
30539     }
30540 };
30541 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
30542     orientation: Roo.SplitBar.VERTICAL,
30543     getBox : function(){
30544         if(this.collapsed){
30545             return this.collapsedEl.getBox();
30546         }
30547         var box = this.el.getBox();
30548         if(this.split){
30549             box.height += this.split.el.getHeight();
30550         }
30551         return box;
30552     },
30553     
30554     updateBox : function(box){
30555         if(this.split && !this.collapsed){
30556             box.height -= this.split.el.getHeight();
30557             this.split.el.setLeft(box.x);
30558             this.split.el.setTop(box.y+box.height);
30559             this.split.el.setWidth(box.width);
30560         }
30561         if(this.collapsed){
30562             this.updateBody(box.width, null);
30563         }
30564         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30565     }
30566 });
30567
30568 Roo.SouthLayoutRegion = function(mgr, config){
30569     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
30570     if(this.split){
30571         this.split.placement = Roo.SplitBar.BOTTOM;
30572         this.split.orientation = Roo.SplitBar.VERTICAL;
30573         this.split.el.addClass("x-layout-split-v");
30574     }
30575     var size = config.initialSize || config.height;
30576     if(typeof size != "undefined"){
30577         this.el.setHeight(size);
30578     }
30579 };
30580 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
30581     orientation: Roo.SplitBar.VERTICAL,
30582     getBox : function(){
30583         if(this.collapsed){
30584             return this.collapsedEl.getBox();
30585         }
30586         var box = this.el.getBox();
30587         if(this.split){
30588             var sh = this.split.el.getHeight();
30589             box.height += sh;
30590             box.y -= sh;
30591         }
30592         return box;
30593     },
30594     
30595     updateBox : function(box){
30596         if(this.split && !this.collapsed){
30597             var sh = this.split.el.getHeight();
30598             box.height -= sh;
30599             box.y += sh;
30600             this.split.el.setLeft(box.x);
30601             this.split.el.setTop(box.y-sh);
30602             this.split.el.setWidth(box.width);
30603         }
30604         if(this.collapsed){
30605             this.updateBody(box.width, null);
30606         }
30607         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30608     }
30609 });
30610
30611 Roo.EastLayoutRegion = function(mgr, config){
30612     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
30613     if(this.split){
30614         this.split.placement = Roo.SplitBar.RIGHT;
30615         this.split.orientation = Roo.SplitBar.HORIZONTAL;
30616         this.split.el.addClass("x-layout-split-h");
30617     }
30618     var size = config.initialSize || config.width;
30619     if(typeof size != "undefined"){
30620         this.el.setWidth(size);
30621     }
30622 };
30623 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
30624     orientation: Roo.SplitBar.HORIZONTAL,
30625     getBox : function(){
30626         if(this.collapsed){
30627             return this.collapsedEl.getBox();
30628         }
30629         var box = this.el.getBox();
30630         if(this.split){
30631             var sw = this.split.el.getWidth();
30632             box.width += sw;
30633             box.x -= sw;
30634         }
30635         return box;
30636     },
30637
30638     updateBox : function(box){
30639         if(this.split && !this.collapsed){
30640             var sw = this.split.el.getWidth();
30641             box.width -= sw;
30642             this.split.el.setLeft(box.x);
30643             this.split.el.setTop(box.y);
30644             this.split.el.setHeight(box.height);
30645             box.x += sw;
30646         }
30647         if(this.collapsed){
30648             this.updateBody(null, box.height);
30649         }
30650         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30651     }
30652 });
30653
30654 Roo.WestLayoutRegion = function(mgr, config){
30655     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
30656     if(this.split){
30657         this.split.placement = Roo.SplitBar.LEFT;
30658         this.split.orientation = Roo.SplitBar.HORIZONTAL;
30659         this.split.el.addClass("x-layout-split-h");
30660     }
30661     var size = config.initialSize || config.width;
30662     if(typeof size != "undefined"){
30663         this.el.setWidth(size);
30664     }
30665 };
30666 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
30667     orientation: Roo.SplitBar.HORIZONTAL,
30668     getBox : function(){
30669         if(this.collapsed){
30670             return this.collapsedEl.getBox();
30671         }
30672         var box = this.el.getBox();
30673         if(this.split){
30674             box.width += this.split.el.getWidth();
30675         }
30676         return box;
30677     },
30678     
30679     updateBox : function(box){
30680         if(this.split && !this.collapsed){
30681             var sw = this.split.el.getWidth();
30682             box.width -= sw;
30683             this.split.el.setLeft(box.x+box.width);
30684             this.split.el.setTop(box.y);
30685             this.split.el.setHeight(box.height);
30686         }
30687         if(this.collapsed){
30688             this.updateBody(null, box.height);
30689         }
30690         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30691     }
30692 });
30693 /*
30694  * Based on:
30695  * Ext JS Library 1.1.1
30696  * Copyright(c) 2006-2007, Ext JS, LLC.
30697  *
30698  * Originally Released Under LGPL - original licence link has changed is not relivant.
30699  *
30700  * Fork - LGPL
30701  * <script type="text/javascript">
30702  */
30703  
30704  
30705 /*
30706  * Private internal class for reading and applying state
30707  */
30708 Roo.LayoutStateManager = function(layout){
30709      // default empty state
30710      this.state = {
30711         north: {},
30712         south: {},
30713         east: {},
30714         west: {}       
30715     };
30716 };
30717
30718 Roo.LayoutStateManager.prototype = {
30719     init : function(layout, provider){
30720         this.provider = provider;
30721         var state = provider.get(layout.id+"-layout-state");
30722         if(state){
30723             var wasUpdating = layout.isUpdating();
30724             if(!wasUpdating){
30725                 layout.beginUpdate();
30726             }
30727             for(var key in state){
30728                 if(typeof state[key] != "function"){
30729                     var rstate = state[key];
30730                     var r = layout.getRegion(key);
30731                     if(r && rstate){
30732                         if(rstate.size){
30733                             r.resizeTo(rstate.size);
30734                         }
30735                         if(rstate.collapsed == true){
30736                             r.collapse(true);
30737                         }else{
30738                             r.expand(null, true);
30739                         }
30740                     }
30741                 }
30742             }
30743             if(!wasUpdating){
30744                 layout.endUpdate();
30745             }
30746             this.state = state; 
30747         }
30748         this.layout = layout;
30749         layout.on("regionresized", this.onRegionResized, this);
30750         layout.on("regioncollapsed", this.onRegionCollapsed, this);
30751         layout.on("regionexpanded", this.onRegionExpanded, this);
30752     },
30753     
30754     storeState : function(){
30755         this.provider.set(this.layout.id+"-layout-state", this.state);
30756     },
30757     
30758     onRegionResized : function(region, newSize){
30759         this.state[region.getPosition()].size = newSize;
30760         this.storeState();
30761     },
30762     
30763     onRegionCollapsed : function(region){
30764         this.state[region.getPosition()].collapsed = true;
30765         this.storeState();
30766     },
30767     
30768     onRegionExpanded : function(region){
30769         this.state[region.getPosition()].collapsed = false;
30770         this.storeState();
30771     }
30772 };/*
30773  * Based on:
30774  * Ext JS Library 1.1.1
30775  * Copyright(c) 2006-2007, Ext JS, LLC.
30776  *
30777  * Originally Released Under LGPL - original licence link has changed is not relivant.
30778  *
30779  * Fork - LGPL
30780  * <script type="text/javascript">
30781  */
30782 /**
30783  * @class Roo.ContentPanel
30784  * @extends Roo.util.Observable
30785  * A basic ContentPanel element.
30786  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
30787  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
30788  * @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
30789  * @cfg {Boolean}   closable      True if the panel can be closed/removed
30790  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
30791  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
30792  * @cfg {Toolbar}   toolbar       A toolbar for this panel
30793  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
30794  * @cfg {String} title          The title for this panel
30795  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
30796  * @cfg {String} url            Calls {@link #setUrl} with this value
30797  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
30798  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
30799  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
30800  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
30801
30802  * @constructor
30803  * Create a new ContentPanel.
30804  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
30805  * @param {String/Object} config A string to set only the title or a config object
30806  * @param {String} content (optional) Set the HTML content for this panel
30807  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
30808  */
30809 Roo.ContentPanel = function(el, config, content){
30810     
30811      
30812     /*
30813     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
30814         config = el;
30815         el = Roo.id();
30816     }
30817     if (config && config.parentLayout) { 
30818         el = config.parentLayout.el.createChild(); 
30819     }
30820     */
30821     if(el.autoCreate){ // xtype is available if this is called from factory
30822         config = el;
30823         el = Roo.id();
30824     }
30825     this.el = Roo.get(el);
30826     if(!this.el && config && config.autoCreate){
30827         if(typeof config.autoCreate == "object"){
30828             if(!config.autoCreate.id){
30829                 config.autoCreate.id = config.id||el;
30830             }
30831             this.el = Roo.DomHelper.append(document.body,
30832                         config.autoCreate, true);
30833         }else{
30834             this.el = Roo.DomHelper.append(document.body,
30835                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
30836         }
30837     }
30838     this.closable = false;
30839     this.loaded = false;
30840     this.active = false;
30841     if(typeof config == "string"){
30842         this.title = config;
30843     }else{
30844         Roo.apply(this, config);
30845     }
30846     
30847     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
30848         this.wrapEl = this.el.wrap();
30849         this.toolbar.container = this.el.insertSibling(false, 'before');
30850         this.toolbar = new Roo.Toolbar(this.toolbar);
30851     }
30852     
30853     // xtype created footer. - not sure if will work as we normally have to render first..
30854     if (this.footer && !this.footer.el && this.footer.xtype) {
30855         if (!this.wrapEl) {
30856             this.wrapEl = this.el.wrap();
30857         }
30858     
30859         this.footer.container = this.wrapEl.createChild();
30860          
30861         this.footer = Roo.factory(this.footer, Roo);
30862         
30863     }
30864     
30865     if(this.resizeEl){
30866         this.resizeEl = Roo.get(this.resizeEl, true);
30867     }else{
30868         this.resizeEl = this.el;
30869     }
30870     // handle view.xtype
30871     
30872  
30873     
30874     
30875     this.addEvents({
30876         /**
30877          * @event activate
30878          * Fires when this panel is activated. 
30879          * @param {Roo.ContentPanel} this
30880          */
30881         "activate" : true,
30882         /**
30883          * @event deactivate
30884          * Fires when this panel is activated. 
30885          * @param {Roo.ContentPanel} this
30886          */
30887         "deactivate" : true,
30888
30889         /**
30890          * @event resize
30891          * Fires when this panel is resized if fitToFrame is true.
30892          * @param {Roo.ContentPanel} this
30893          * @param {Number} width The width after any component adjustments
30894          * @param {Number} height The height after any component adjustments
30895          */
30896         "resize" : true,
30897         
30898          /**
30899          * @event render
30900          * Fires when this tab is created
30901          * @param {Roo.ContentPanel} this
30902          */
30903         "render" : true
30904         
30905         
30906         
30907     });
30908     
30909
30910     
30911     
30912     if(this.autoScroll){
30913         this.resizeEl.setStyle("overflow", "auto");
30914     } else {
30915         // fix randome scrolling
30916         this.el.on('scroll', function() {
30917             Roo.log('fix random scolling');
30918             this.scrollTo('top',0); 
30919         });
30920     }
30921     content = content || this.content;
30922     if(content){
30923         this.setContent(content);
30924     }
30925     if(config && config.url){
30926         this.setUrl(this.url, this.params, this.loadOnce);
30927     }
30928     
30929     
30930     
30931     Roo.ContentPanel.superclass.constructor.call(this);
30932     
30933     if (this.view && typeof(this.view.xtype) != 'undefined') {
30934         this.view.el = this.el.appendChild(document.createElement("div"));
30935         this.view = Roo.factory(this.view); 
30936         this.view.render  &&  this.view.render(false, '');  
30937     }
30938     
30939     
30940     this.fireEvent('render', this);
30941 };
30942
30943 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
30944     tabTip:'',
30945     setRegion : function(region){
30946         this.region = region;
30947         if(region){
30948            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
30949         }else{
30950            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
30951         } 
30952     },
30953     
30954     /**
30955      * Returns the toolbar for this Panel if one was configured. 
30956      * @return {Roo.Toolbar} 
30957      */
30958     getToolbar : function(){
30959         return this.toolbar;
30960     },
30961     
30962     setActiveState : function(active){
30963         this.active = active;
30964         if(!active){
30965             this.fireEvent("deactivate", this);
30966         }else{
30967             this.fireEvent("activate", this);
30968         }
30969     },
30970     /**
30971      * Updates this panel's element
30972      * @param {String} content The new content
30973      * @param {Boolean} loadScripts (optional) true to look for and process scripts
30974     */
30975     setContent : function(content, loadScripts){
30976         this.el.update(content, loadScripts);
30977     },
30978
30979     ignoreResize : function(w, h){
30980         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
30981             return true;
30982         }else{
30983             this.lastSize = {width: w, height: h};
30984             return false;
30985         }
30986     },
30987     /**
30988      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
30989      * @return {Roo.UpdateManager} The UpdateManager
30990      */
30991     getUpdateManager : function(){
30992         return this.el.getUpdateManager();
30993     },
30994      /**
30995      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
30996      * @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:
30997 <pre><code>
30998 panel.load({
30999     url: "your-url.php",
31000     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
31001     callback: yourFunction,
31002     scope: yourObject, //(optional scope)
31003     discardUrl: false,
31004     nocache: false,
31005     text: "Loading...",
31006     timeout: 30,
31007     scripts: false
31008 });
31009 </code></pre>
31010      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
31011      * 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.
31012      * @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}
31013      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
31014      * @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.
31015      * @return {Roo.ContentPanel} this
31016      */
31017     load : function(){
31018         var um = this.el.getUpdateManager();
31019         um.update.apply(um, arguments);
31020         return this;
31021     },
31022
31023
31024     /**
31025      * 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.
31026      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
31027      * @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)
31028      * @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)
31029      * @return {Roo.UpdateManager} The UpdateManager
31030      */
31031     setUrl : function(url, params, loadOnce){
31032         if(this.refreshDelegate){
31033             this.removeListener("activate", this.refreshDelegate);
31034         }
31035         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
31036         this.on("activate", this.refreshDelegate);
31037         return this.el.getUpdateManager();
31038     },
31039     
31040     _handleRefresh : function(url, params, loadOnce){
31041         if(!loadOnce || !this.loaded){
31042             var updater = this.el.getUpdateManager();
31043             updater.update(url, params, this._setLoaded.createDelegate(this));
31044         }
31045     },
31046     
31047     _setLoaded : function(){
31048         this.loaded = true;
31049     }, 
31050     
31051     /**
31052      * Returns this panel's id
31053      * @return {String} 
31054      */
31055     getId : function(){
31056         return this.el.id;
31057     },
31058     
31059     /** 
31060      * Returns this panel's element - used by regiosn to add.
31061      * @return {Roo.Element} 
31062      */
31063     getEl : function(){
31064         return this.wrapEl || this.el;
31065     },
31066     
31067     adjustForComponents : function(width, height)
31068     {
31069         //Roo.log('adjustForComponents ');
31070         if(this.resizeEl != this.el){
31071             width -= this.el.getFrameWidth('lr');
31072             height -= this.el.getFrameWidth('tb');
31073         }
31074         if(this.toolbar){
31075             var te = this.toolbar.getEl();
31076             height -= te.getHeight();
31077             te.setWidth(width);
31078         }
31079         if(this.footer){
31080             var te = this.footer.getEl();
31081             Roo.log("footer:" + te.getHeight());
31082             
31083             height -= te.getHeight();
31084             te.setWidth(width);
31085         }
31086         
31087         
31088         if(this.adjustments){
31089             width += this.adjustments[0];
31090             height += this.adjustments[1];
31091         }
31092         return {"width": width, "height": height};
31093     },
31094     
31095     setSize : function(width, height){
31096         if(this.fitToFrame && !this.ignoreResize(width, height)){
31097             if(this.fitContainer && this.resizeEl != this.el){
31098                 this.el.setSize(width, height);
31099             }
31100             var size = this.adjustForComponents(width, height);
31101             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
31102             this.fireEvent('resize', this, size.width, size.height);
31103         }
31104     },
31105     
31106     /**
31107      * Returns this panel's title
31108      * @return {String} 
31109      */
31110     getTitle : function(){
31111         return this.title;
31112     },
31113     
31114     /**
31115      * Set this panel's title
31116      * @param {String} title
31117      */
31118     setTitle : function(title){
31119         this.title = title;
31120         if(this.region){
31121             this.region.updatePanelTitle(this, title);
31122         }
31123     },
31124     
31125     /**
31126      * Returns true is this panel was configured to be closable
31127      * @return {Boolean} 
31128      */
31129     isClosable : function(){
31130         return this.closable;
31131     },
31132     
31133     beforeSlide : function(){
31134         this.el.clip();
31135         this.resizeEl.clip();
31136     },
31137     
31138     afterSlide : function(){
31139         this.el.unclip();
31140         this.resizeEl.unclip();
31141     },
31142     
31143     /**
31144      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
31145      *   Will fail silently if the {@link #setUrl} method has not been called.
31146      *   This does not activate the panel, just updates its content.
31147      */
31148     refresh : function(){
31149         if(this.refreshDelegate){
31150            this.loaded = false;
31151            this.refreshDelegate();
31152         }
31153     },
31154     
31155     /**
31156      * Destroys this panel
31157      */
31158     destroy : function(){
31159         this.el.removeAllListeners();
31160         var tempEl = document.createElement("span");
31161         tempEl.appendChild(this.el.dom);
31162         tempEl.innerHTML = "";
31163         this.el.remove();
31164         this.el = null;
31165     },
31166     
31167     /**
31168      * form - if the content panel contains a form - this is a reference to it.
31169      * @type {Roo.form.Form}
31170      */
31171     form : false,
31172     /**
31173      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
31174      *    This contains a reference to it.
31175      * @type {Roo.View}
31176      */
31177     view : false,
31178     
31179       /**
31180      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
31181      * <pre><code>
31182
31183 layout.addxtype({
31184        xtype : 'Form',
31185        items: [ .... ]
31186    }
31187 );
31188
31189 </code></pre>
31190      * @param {Object} cfg Xtype definition of item to add.
31191      */
31192     
31193     addxtype : function(cfg) {
31194         // add form..
31195         if (cfg.xtype.match(/^Form$/)) {
31196             
31197             var el;
31198             //if (this.footer) {
31199             //    el = this.footer.container.insertSibling(false, 'before');
31200             //} else {
31201                 el = this.el.createChild();
31202             //}
31203
31204             this.form = new  Roo.form.Form(cfg);
31205             
31206             
31207             if ( this.form.allItems.length) {
31208                 this.form.render(el.dom);
31209             }
31210             return this.form;
31211         }
31212         // should only have one of theses..
31213         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
31214             // views.. should not be just added - used named prop 'view''
31215             
31216             cfg.el = this.el.appendChild(document.createElement("div"));
31217             // factory?
31218             
31219             var ret = new Roo.factory(cfg);
31220              
31221              ret.render && ret.render(false, ''); // render blank..
31222             this.view = ret;
31223             return ret;
31224         }
31225         return false;
31226     }
31227 });
31228
31229 /**
31230  * @class Roo.GridPanel
31231  * @extends Roo.ContentPanel
31232  * @constructor
31233  * Create a new GridPanel.
31234  * @param {Roo.grid.Grid} grid The grid for this panel
31235  * @param {String/Object} config A string to set only the panel's title, or a config object
31236  */
31237 Roo.GridPanel = function(grid, config){
31238     
31239   
31240     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
31241         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
31242         
31243     this.wrapper.dom.appendChild(grid.getGridEl().dom);
31244     
31245     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
31246     
31247     if(this.toolbar){
31248         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
31249     }
31250     // xtype created footer. - not sure if will work as we normally have to render first..
31251     if (this.footer && !this.footer.el && this.footer.xtype) {
31252         
31253         this.footer.container = this.grid.getView().getFooterPanel(true);
31254         this.footer.dataSource = this.grid.dataSource;
31255         this.footer = Roo.factory(this.footer, Roo);
31256         
31257     }
31258     
31259     grid.monitorWindowResize = false; // turn off autosizing
31260     grid.autoHeight = false;
31261     grid.autoWidth = false;
31262     this.grid = grid;
31263     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
31264 };
31265
31266 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
31267     getId : function(){
31268         return this.grid.id;
31269     },
31270     
31271     /**
31272      * Returns the grid for this panel
31273      * @return {Roo.grid.Grid} 
31274      */
31275     getGrid : function(){
31276         return this.grid;    
31277     },
31278     
31279     setSize : function(width, height){
31280         if(!this.ignoreResize(width, height)){
31281             var grid = this.grid;
31282             var size = this.adjustForComponents(width, height);
31283             grid.getGridEl().setSize(size.width, size.height);
31284             grid.autoSize();
31285         }
31286     },
31287     
31288     beforeSlide : function(){
31289         this.grid.getView().scroller.clip();
31290     },
31291     
31292     afterSlide : function(){
31293         this.grid.getView().scroller.unclip();
31294     },
31295     
31296     destroy : function(){
31297         this.grid.destroy();
31298         delete this.grid;
31299         Roo.GridPanel.superclass.destroy.call(this); 
31300     }
31301 });
31302
31303
31304 /**
31305  * @class Roo.NestedLayoutPanel
31306  * @extends Roo.ContentPanel
31307  * @constructor
31308  * Create a new NestedLayoutPanel.
31309  * 
31310  * 
31311  * @param {Roo.BorderLayout} layout The layout for this panel
31312  * @param {String/Object} config A string to set only the title or a config object
31313  */
31314 Roo.NestedLayoutPanel = function(layout, config)
31315 {
31316     // construct with only one argument..
31317     /* FIXME - implement nicer consturctors
31318     if (layout.layout) {
31319         config = layout;
31320         layout = config.layout;
31321         delete config.layout;
31322     }
31323     if (layout.xtype && !layout.getEl) {
31324         // then layout needs constructing..
31325         layout = Roo.factory(layout, Roo);
31326     }
31327     */
31328     
31329     
31330     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
31331     
31332     layout.monitorWindowResize = false; // turn off autosizing
31333     this.layout = layout;
31334     this.layout.getEl().addClass("x-layout-nested-layout");
31335     
31336     
31337     
31338     
31339 };
31340
31341 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
31342
31343     setSize : function(width, height){
31344         if(!this.ignoreResize(width, height)){
31345             var size = this.adjustForComponents(width, height);
31346             var el = this.layout.getEl();
31347             el.setSize(size.width, size.height);
31348             var touch = el.dom.offsetWidth;
31349             this.layout.layout();
31350             // ie requires a double layout on the first pass
31351             if(Roo.isIE && !this.initialized){
31352                 this.initialized = true;
31353                 this.layout.layout();
31354             }
31355         }
31356     },
31357     
31358     // activate all subpanels if not currently active..
31359     
31360     setActiveState : function(active){
31361         this.active = active;
31362         if(!active){
31363             this.fireEvent("deactivate", this);
31364             return;
31365         }
31366         
31367         this.fireEvent("activate", this);
31368         // not sure if this should happen before or after..
31369         if (!this.layout) {
31370             return; // should not happen..
31371         }
31372         var reg = false;
31373         for (var r in this.layout.regions) {
31374             reg = this.layout.getRegion(r);
31375             if (reg.getActivePanel()) {
31376                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
31377                 reg.setActivePanel(reg.getActivePanel());
31378                 continue;
31379             }
31380             if (!reg.panels.length) {
31381                 continue;
31382             }
31383             reg.showPanel(reg.getPanel(0));
31384         }
31385         
31386         
31387         
31388         
31389     },
31390     
31391     /**
31392      * Returns the nested BorderLayout for this panel
31393      * @return {Roo.BorderLayout} 
31394      */
31395     getLayout : function(){
31396         return this.layout;
31397     },
31398     
31399      /**
31400      * Adds a xtype elements to the layout of the nested panel
31401      * <pre><code>
31402
31403 panel.addxtype({
31404        xtype : 'ContentPanel',
31405        region: 'west',
31406        items: [ .... ]
31407    }
31408 );
31409
31410 panel.addxtype({
31411         xtype : 'NestedLayoutPanel',
31412         region: 'west',
31413         layout: {
31414            center: { },
31415            west: { }   
31416         },
31417         items : [ ... list of content panels or nested layout panels.. ]
31418    }
31419 );
31420 </code></pre>
31421      * @param {Object} cfg Xtype definition of item to add.
31422      */
31423     addxtype : function(cfg) {
31424         return this.layout.addxtype(cfg);
31425     
31426     }
31427 });
31428
31429 Roo.ScrollPanel = function(el, config, content){
31430     config = config || {};
31431     config.fitToFrame = true;
31432     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
31433     
31434     this.el.dom.style.overflow = "hidden";
31435     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
31436     this.el.removeClass("x-layout-inactive-content");
31437     this.el.on("mousewheel", this.onWheel, this);
31438
31439     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
31440     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
31441     up.unselectable(); down.unselectable();
31442     up.on("click", this.scrollUp, this);
31443     down.on("click", this.scrollDown, this);
31444     up.addClassOnOver("x-scroller-btn-over");
31445     down.addClassOnOver("x-scroller-btn-over");
31446     up.addClassOnClick("x-scroller-btn-click");
31447     down.addClassOnClick("x-scroller-btn-click");
31448     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
31449
31450     this.resizeEl = this.el;
31451     this.el = wrap; this.up = up; this.down = down;
31452 };
31453
31454 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
31455     increment : 100,
31456     wheelIncrement : 5,
31457     scrollUp : function(){
31458         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
31459     },
31460
31461     scrollDown : function(){
31462         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
31463     },
31464
31465     afterScroll : function(){
31466         var el = this.resizeEl;
31467         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
31468         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31469         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31470     },
31471
31472     setSize : function(){
31473         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
31474         this.afterScroll();
31475     },
31476
31477     onWheel : function(e){
31478         var d = e.getWheelDelta();
31479         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
31480         this.afterScroll();
31481         e.stopEvent();
31482     },
31483
31484     setContent : function(content, loadScripts){
31485         this.resizeEl.update(content, loadScripts);
31486     }
31487
31488 });
31489
31490
31491
31492
31493
31494
31495
31496
31497
31498 /**
31499  * @class Roo.TreePanel
31500  * @extends Roo.ContentPanel
31501  * @constructor
31502  * Create a new TreePanel. - defaults to fit/scoll contents.
31503  * @param {String/Object} config A string to set only the panel's title, or a config object
31504  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
31505  */
31506 Roo.TreePanel = function(config){
31507     var el = config.el;
31508     var tree = config.tree;
31509     delete config.tree; 
31510     delete config.el; // hopefull!
31511     
31512     // wrapper for IE7 strict & safari scroll issue
31513     
31514     var treeEl = el.createChild();
31515     config.resizeEl = treeEl;
31516     
31517     
31518     
31519     Roo.TreePanel.superclass.constructor.call(this, el, config);
31520  
31521  
31522     this.tree = new Roo.tree.TreePanel(treeEl , tree);
31523     //console.log(tree);
31524     this.on('activate', function()
31525     {
31526         if (this.tree.rendered) {
31527             return;
31528         }
31529         //console.log('render tree');
31530         this.tree.render();
31531     });
31532     // this should not be needed.. - it's actually the 'el' that resizes?
31533     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
31534     
31535     //this.on('resize',  function (cp, w, h) {
31536     //        this.tree.innerCt.setWidth(w);
31537     //        this.tree.innerCt.setHeight(h);
31538     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
31539     //});
31540
31541         
31542     
31543 };
31544
31545 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
31546     fitToFrame : true,
31547     autoScroll : true
31548 });
31549
31550
31551
31552
31553
31554
31555
31556
31557
31558
31559
31560 /*
31561  * Based on:
31562  * Ext JS Library 1.1.1
31563  * Copyright(c) 2006-2007, Ext JS, LLC.
31564  *
31565  * Originally Released Under LGPL - original licence link has changed is not relivant.
31566  *
31567  * Fork - LGPL
31568  * <script type="text/javascript">
31569  */
31570  
31571
31572 /**
31573  * @class Roo.ReaderLayout
31574  * @extends Roo.BorderLayout
31575  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
31576  * center region containing two nested regions (a top one for a list view and one for item preview below),
31577  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
31578  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
31579  * expedites the setup of the overall layout and regions for this common application style.
31580  * Example:
31581  <pre><code>
31582 var reader = new Roo.ReaderLayout();
31583 var CP = Roo.ContentPanel;  // shortcut for adding
31584
31585 reader.beginUpdate();
31586 reader.add("north", new CP("north", "North"));
31587 reader.add("west", new CP("west", {title: "West"}));
31588 reader.add("east", new CP("east", {title: "East"}));
31589
31590 reader.regions.listView.add(new CP("listView", "List"));
31591 reader.regions.preview.add(new CP("preview", "Preview"));
31592 reader.endUpdate();
31593 </code></pre>
31594 * @constructor
31595 * Create a new ReaderLayout
31596 * @param {Object} config Configuration options
31597 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
31598 * document.body if omitted)
31599 */
31600 Roo.ReaderLayout = function(config, renderTo){
31601     var c = config || {size:{}};
31602     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
31603         north: c.north !== false ? Roo.apply({
31604             split:false,
31605             initialSize: 32,
31606             titlebar: false
31607         }, c.north) : false,
31608         west: c.west !== false ? Roo.apply({
31609             split:true,
31610             initialSize: 200,
31611             minSize: 175,
31612             maxSize: 400,
31613             titlebar: true,
31614             collapsible: true,
31615             animate: true,
31616             margins:{left:5,right:0,bottom:5,top:5},
31617             cmargins:{left:5,right:5,bottom:5,top:5}
31618         }, c.west) : false,
31619         east: c.east !== false ? Roo.apply({
31620             split:true,
31621             initialSize: 200,
31622             minSize: 175,
31623             maxSize: 400,
31624             titlebar: true,
31625             collapsible: true,
31626             animate: true,
31627             margins:{left:0,right:5,bottom:5,top:5},
31628             cmargins:{left:5,right:5,bottom:5,top:5}
31629         }, c.east) : false,
31630         center: Roo.apply({
31631             tabPosition: 'top',
31632             autoScroll:false,
31633             closeOnTab: true,
31634             titlebar:false,
31635             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
31636         }, c.center)
31637     });
31638
31639     this.el.addClass('x-reader');
31640
31641     this.beginUpdate();
31642
31643     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
31644         south: c.preview !== false ? Roo.apply({
31645             split:true,
31646             initialSize: 200,
31647             minSize: 100,
31648             autoScroll:true,
31649             collapsible:true,
31650             titlebar: true,
31651             cmargins:{top:5,left:0, right:0, bottom:0}
31652         }, c.preview) : false,
31653         center: Roo.apply({
31654             autoScroll:false,
31655             titlebar:false,
31656             minHeight:200
31657         }, c.listView)
31658     });
31659     this.add('center', new Roo.NestedLayoutPanel(inner,
31660             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
31661
31662     this.endUpdate();
31663
31664     this.regions.preview = inner.getRegion('south');
31665     this.regions.listView = inner.getRegion('center');
31666 };
31667
31668 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
31669  * Based on:
31670  * Ext JS Library 1.1.1
31671  * Copyright(c) 2006-2007, Ext JS, LLC.
31672  *
31673  * Originally Released Under LGPL - original licence link has changed is not relivant.
31674  *
31675  * Fork - LGPL
31676  * <script type="text/javascript">
31677  */
31678  
31679 /**
31680  * @class Roo.grid.Grid
31681  * @extends Roo.util.Observable
31682  * This class represents the primary interface of a component based grid control.
31683  * <br><br>Usage:<pre><code>
31684  var grid = new Roo.grid.Grid("my-container-id", {
31685      ds: myDataStore,
31686      cm: myColModel,
31687      selModel: mySelectionModel,
31688      autoSizeColumns: true,
31689      monitorWindowResize: false,
31690      trackMouseOver: true
31691  });
31692  // set any options
31693  grid.render();
31694  * </code></pre>
31695  * <b>Common Problems:</b><br/>
31696  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
31697  * element will correct this<br/>
31698  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
31699  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
31700  * are unpredictable.<br/>
31701  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
31702  * grid to calculate dimensions/offsets.<br/>
31703   * @constructor
31704  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
31705  * The container MUST have some type of size defined for the grid to fill. The container will be
31706  * automatically set to position relative if it isn't already.
31707  * @param {Object} config A config object that sets properties on this grid.
31708  */
31709 Roo.grid.Grid = function(container, config){
31710         // initialize the container
31711         this.container = Roo.get(container);
31712         this.container.update("");
31713         this.container.setStyle("overflow", "hidden");
31714     this.container.addClass('x-grid-container');
31715
31716     this.id = this.container.id;
31717
31718     Roo.apply(this, config);
31719     // check and correct shorthanded configs
31720     if(this.ds){
31721         this.dataSource = this.ds;
31722         delete this.ds;
31723     }
31724     if(this.cm){
31725         this.colModel = this.cm;
31726         delete this.cm;
31727     }
31728     if(this.sm){
31729         this.selModel = this.sm;
31730         delete this.sm;
31731     }
31732
31733     if (this.selModel) {
31734         this.selModel = Roo.factory(this.selModel, Roo.grid);
31735         this.sm = this.selModel;
31736         this.sm.xmodule = this.xmodule || false;
31737     }
31738     if (typeof(this.colModel.config) == 'undefined') {
31739         this.colModel = new Roo.grid.ColumnModel(this.colModel);
31740         this.cm = this.colModel;
31741         this.cm.xmodule = this.xmodule || false;
31742     }
31743     if (this.dataSource) {
31744         this.dataSource= Roo.factory(this.dataSource, Roo.data);
31745         this.ds = this.dataSource;
31746         this.ds.xmodule = this.xmodule || false;
31747          
31748     }
31749     
31750     
31751     
31752     if(this.width){
31753         this.container.setWidth(this.width);
31754     }
31755
31756     if(this.height){
31757         this.container.setHeight(this.height);
31758     }
31759     /** @private */
31760         this.addEvents({
31761         // raw events
31762         /**
31763          * @event click
31764          * The raw click event for the entire grid.
31765          * @param {Roo.EventObject} e
31766          */
31767         "click" : true,
31768         /**
31769          * @event dblclick
31770          * The raw dblclick event for the entire grid.
31771          * @param {Roo.EventObject} e
31772          */
31773         "dblclick" : true,
31774         /**
31775          * @event contextmenu
31776          * The raw contextmenu event for the entire grid.
31777          * @param {Roo.EventObject} e
31778          */
31779         "contextmenu" : true,
31780         /**
31781          * @event mousedown
31782          * The raw mousedown event for the entire grid.
31783          * @param {Roo.EventObject} e
31784          */
31785         "mousedown" : true,
31786         /**
31787          * @event mouseup
31788          * The raw mouseup event for the entire grid.
31789          * @param {Roo.EventObject} e
31790          */
31791         "mouseup" : true,
31792         /**
31793          * @event mouseover
31794          * The raw mouseover event for the entire grid.
31795          * @param {Roo.EventObject} e
31796          */
31797         "mouseover" : true,
31798         /**
31799          * @event mouseout
31800          * The raw mouseout event for the entire grid.
31801          * @param {Roo.EventObject} e
31802          */
31803         "mouseout" : true,
31804         /**
31805          * @event keypress
31806          * The raw keypress event for the entire grid.
31807          * @param {Roo.EventObject} e
31808          */
31809         "keypress" : true,
31810         /**
31811          * @event keydown
31812          * The raw keydown event for the entire grid.
31813          * @param {Roo.EventObject} e
31814          */
31815         "keydown" : true,
31816
31817         // custom events
31818
31819         /**
31820          * @event cellclick
31821          * Fires when a cell is clicked
31822          * @param {Grid} this
31823          * @param {Number} rowIndex
31824          * @param {Number} columnIndex
31825          * @param {Roo.EventObject} e
31826          */
31827         "cellclick" : true,
31828         /**
31829          * @event celldblclick
31830          * Fires when a cell is double clicked
31831          * @param {Grid} this
31832          * @param {Number} rowIndex
31833          * @param {Number} columnIndex
31834          * @param {Roo.EventObject} e
31835          */
31836         "celldblclick" : true,
31837         /**
31838          * @event rowclick
31839          * Fires when a row is clicked
31840          * @param {Grid} this
31841          * @param {Number} rowIndex
31842          * @param {Roo.EventObject} e
31843          */
31844         "rowclick" : true,
31845         /**
31846          * @event rowdblclick
31847          * Fires when a row is double clicked
31848          * @param {Grid} this
31849          * @param {Number} rowIndex
31850          * @param {Roo.EventObject} e
31851          */
31852         "rowdblclick" : true,
31853         /**
31854          * @event headerclick
31855          * Fires when a header is clicked
31856          * @param {Grid} this
31857          * @param {Number} columnIndex
31858          * @param {Roo.EventObject} e
31859          */
31860         "headerclick" : true,
31861         /**
31862          * @event headerdblclick
31863          * Fires when a header cell is double clicked
31864          * @param {Grid} this
31865          * @param {Number} columnIndex
31866          * @param {Roo.EventObject} e
31867          */
31868         "headerdblclick" : true,
31869         /**
31870          * @event rowcontextmenu
31871          * Fires when a row is right clicked
31872          * @param {Grid} this
31873          * @param {Number} rowIndex
31874          * @param {Roo.EventObject} e
31875          */
31876         "rowcontextmenu" : true,
31877         /**
31878          * @event cellcontextmenu
31879          * Fires when a cell is right clicked
31880          * @param {Grid} this
31881          * @param {Number} rowIndex
31882          * @param {Number} cellIndex
31883          * @param {Roo.EventObject} e
31884          */
31885          "cellcontextmenu" : true,
31886         /**
31887          * @event headercontextmenu
31888          * Fires when a header is right clicked
31889          * @param {Grid} this
31890          * @param {Number} columnIndex
31891          * @param {Roo.EventObject} e
31892          */
31893         "headercontextmenu" : true,
31894         /**
31895          * @event bodyscroll
31896          * Fires when the body element is scrolled
31897          * @param {Number} scrollLeft
31898          * @param {Number} scrollTop
31899          */
31900         "bodyscroll" : true,
31901         /**
31902          * @event columnresize
31903          * Fires when the user resizes a column
31904          * @param {Number} columnIndex
31905          * @param {Number} newSize
31906          */
31907         "columnresize" : true,
31908         /**
31909          * @event columnmove
31910          * Fires when the user moves a column
31911          * @param {Number} oldIndex
31912          * @param {Number} newIndex
31913          */
31914         "columnmove" : true,
31915         /**
31916          * @event startdrag
31917          * Fires when row(s) start being dragged
31918          * @param {Grid} this
31919          * @param {Roo.GridDD} dd The drag drop object
31920          * @param {event} e The raw browser event
31921          */
31922         "startdrag" : true,
31923         /**
31924          * @event enddrag
31925          * Fires when a drag operation is complete
31926          * @param {Grid} this
31927          * @param {Roo.GridDD} dd The drag drop object
31928          * @param {event} e The raw browser event
31929          */
31930         "enddrag" : true,
31931         /**
31932          * @event dragdrop
31933          * Fires when dragged row(s) are dropped on a valid DD target
31934          * @param {Grid} this
31935          * @param {Roo.GridDD} dd The drag drop object
31936          * @param {String} targetId The target drag drop object
31937          * @param {event} e The raw browser event
31938          */
31939         "dragdrop" : true,
31940         /**
31941          * @event dragover
31942          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
31943          * @param {Grid} this
31944          * @param {Roo.GridDD} dd The drag drop object
31945          * @param {String} targetId The target drag drop object
31946          * @param {event} e The raw browser event
31947          */
31948         "dragover" : true,
31949         /**
31950          * @event dragenter
31951          *  Fires when the dragged row(s) first cross another DD target while being dragged
31952          * @param {Grid} this
31953          * @param {Roo.GridDD} dd The drag drop object
31954          * @param {String} targetId The target drag drop object
31955          * @param {event} e The raw browser event
31956          */
31957         "dragenter" : true,
31958         /**
31959          * @event dragout
31960          * Fires when the dragged row(s) leave another DD target while being dragged
31961          * @param {Grid} this
31962          * @param {Roo.GridDD} dd The drag drop object
31963          * @param {String} targetId The target drag drop object
31964          * @param {event} e The raw browser event
31965          */
31966         "dragout" : true,
31967         /**
31968          * @event rowclass
31969          * Fires when a row is rendered, so you can change add a style to it.
31970          * @param {GridView} gridview   The grid view
31971          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
31972          */
31973         'rowclass' : true,
31974
31975         /**
31976          * @event render
31977          * Fires when the grid is rendered
31978          * @param {Grid} grid
31979          */
31980         'render' : true
31981     });
31982
31983     Roo.grid.Grid.superclass.constructor.call(this);
31984 };
31985 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
31986     
31987     /**
31988      * @cfg {String} ddGroup - drag drop group.
31989      */
31990
31991     /**
31992      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
31993      */
31994     minColumnWidth : 25,
31995
31996     /**
31997      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
31998      * <b>on initial render.</b> It is more efficient to explicitly size the columns
31999      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
32000      */
32001     autoSizeColumns : false,
32002
32003     /**
32004      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
32005      */
32006     autoSizeHeaders : true,
32007
32008     /**
32009      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
32010      */
32011     monitorWindowResize : true,
32012
32013     /**
32014      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
32015      * rows measured to get a columns size. Default is 0 (all rows).
32016      */
32017     maxRowsToMeasure : 0,
32018
32019     /**
32020      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
32021      */
32022     trackMouseOver : true,
32023
32024     /**
32025     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
32026     */
32027     
32028     /**
32029     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
32030     */
32031     enableDragDrop : false,
32032     
32033     /**
32034     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
32035     */
32036     enableColumnMove : true,
32037     
32038     /**
32039     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
32040     */
32041     enableColumnHide : true,
32042     
32043     /**
32044     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
32045     */
32046     enableRowHeightSync : false,
32047     
32048     /**
32049     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
32050     */
32051     stripeRows : true,
32052     
32053     /**
32054     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
32055     */
32056     autoHeight : false,
32057
32058     /**
32059      * @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.
32060      */
32061     autoExpandColumn : false,
32062
32063     /**
32064     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
32065     * Default is 50.
32066     */
32067     autoExpandMin : 50,
32068
32069     /**
32070     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
32071     */
32072     autoExpandMax : 1000,
32073
32074     /**
32075     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
32076     */
32077     view : null,
32078
32079     /**
32080     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
32081     */
32082     loadMask : false,
32083     /**
32084     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
32085     */
32086     dropTarget: false,
32087     
32088    
32089     
32090     // private
32091     rendered : false,
32092
32093     /**
32094     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
32095     * of a fixed width. Default is false.
32096     */
32097     /**
32098     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
32099     */
32100     /**
32101      * Called once after all setup has been completed and the grid is ready to be rendered.
32102      * @return {Roo.grid.Grid} this
32103      */
32104     render : function()
32105     {
32106         var c = this.container;
32107         // try to detect autoHeight/width mode
32108         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
32109             this.autoHeight = true;
32110         }
32111         var view = this.getView();
32112         view.init(this);
32113
32114         c.on("click", this.onClick, this);
32115         c.on("dblclick", this.onDblClick, this);
32116         c.on("contextmenu", this.onContextMenu, this);
32117         c.on("keydown", this.onKeyDown, this);
32118         if (Roo.isTouch) {
32119             c.on("touchstart", this.onTouchStart, this);
32120         }
32121
32122         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
32123
32124         this.getSelectionModel().init(this);
32125
32126         view.render();
32127
32128         if(this.loadMask){
32129             this.loadMask = new Roo.LoadMask(this.container,
32130                     Roo.apply({store:this.dataSource}, this.loadMask));
32131         }
32132         
32133         
32134         if (this.toolbar && this.toolbar.xtype) {
32135             this.toolbar.container = this.getView().getHeaderPanel(true);
32136             this.toolbar = new Roo.Toolbar(this.toolbar);
32137         }
32138         if (this.footer && this.footer.xtype) {
32139             this.footer.dataSource = this.getDataSource();
32140             this.footer.container = this.getView().getFooterPanel(true);
32141             this.footer = Roo.factory(this.footer, Roo);
32142         }
32143         if (this.dropTarget && this.dropTarget.xtype) {
32144             delete this.dropTarget.xtype;
32145             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
32146         }
32147         
32148         
32149         this.rendered = true;
32150         this.fireEvent('render', this);
32151         return this;
32152     },
32153
32154         /**
32155          * Reconfigures the grid to use a different Store and Column Model.
32156          * The View will be bound to the new objects and refreshed.
32157          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
32158          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
32159          */
32160     reconfigure : function(dataSource, colModel){
32161         if(this.loadMask){
32162             this.loadMask.destroy();
32163             this.loadMask = new Roo.LoadMask(this.container,
32164                     Roo.apply({store:dataSource}, this.loadMask));
32165         }
32166         this.view.bind(dataSource, colModel);
32167         this.dataSource = dataSource;
32168         this.colModel = colModel;
32169         this.view.refresh(true);
32170     },
32171
32172     // private
32173     onKeyDown : function(e){
32174         this.fireEvent("keydown", e);
32175     },
32176
32177     /**
32178      * Destroy this grid.
32179      * @param {Boolean} removeEl True to remove the element
32180      */
32181     destroy : function(removeEl, keepListeners){
32182         if(this.loadMask){
32183             this.loadMask.destroy();
32184         }
32185         var c = this.container;
32186         c.removeAllListeners();
32187         this.view.destroy();
32188         this.colModel.purgeListeners();
32189         if(!keepListeners){
32190             this.purgeListeners();
32191         }
32192         c.update("");
32193         if(removeEl === true){
32194             c.remove();
32195         }
32196     },
32197
32198     // private
32199     processEvent : function(name, e){
32200         // does this fire select???
32201         //Roo.log('grid:processEvent '  + name);
32202         
32203         if (name != 'touchstart' ) {
32204             this.fireEvent(name, e);    
32205         }
32206         
32207         var t = e.getTarget();
32208         var v = this.view;
32209         var header = v.findHeaderIndex(t);
32210         if(header !== false){
32211             var ename = name == 'touchstart' ? 'click' : name;
32212              
32213             this.fireEvent("header" + ename, this, header, e);
32214         }else{
32215             var row = v.findRowIndex(t);
32216             var cell = v.findCellIndex(t);
32217             if (name == 'touchstart') {
32218                 // first touch is always a click.
32219                 // hopefull this happens after selection is updated.?
32220                 name = false;
32221                 
32222                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
32223                     var cs = this.selModel.getSelectedCell();
32224                     if (row == cs[0] && cell == cs[1]){
32225                         name = 'dblclick';
32226                     }
32227                 }
32228                 if (typeof(this.selModel.getSelections) != 'undefined') {
32229                     var cs = this.selModel.getSelections();
32230                     var ds = this.dataSource;
32231                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
32232                         name = 'dblclick';
32233                     }
32234                 }
32235                 if (!name) {
32236                     return;
32237                 }
32238             }
32239             
32240             
32241             if(row !== false){
32242                 this.fireEvent("row" + name, this, row, e);
32243                 if(cell !== false){
32244                     this.fireEvent("cell" + name, this, row, cell, e);
32245                 }
32246             }
32247         }
32248     },
32249
32250     // private
32251     onClick : function(e){
32252         this.processEvent("click", e);
32253     },
32254    // private
32255     onTouchStart : function(e){
32256         this.processEvent("touchstart", e);
32257     },
32258
32259     // private
32260     onContextMenu : function(e, t){
32261         this.processEvent("contextmenu", e);
32262     },
32263
32264     // private
32265     onDblClick : function(e){
32266         this.processEvent("dblclick", e);
32267     },
32268
32269     // private
32270     walkCells : function(row, col, step, fn, scope){
32271         var cm = this.colModel, clen = cm.getColumnCount();
32272         var ds = this.dataSource, rlen = ds.getCount(), first = true;
32273         if(step < 0){
32274             if(col < 0){
32275                 row--;
32276                 first = false;
32277             }
32278             while(row >= 0){
32279                 if(!first){
32280                     col = clen-1;
32281                 }
32282                 first = false;
32283                 while(col >= 0){
32284                     if(fn.call(scope || this, row, col, cm) === true){
32285                         return [row, col];
32286                     }
32287                     col--;
32288                 }
32289                 row--;
32290             }
32291         } else {
32292             if(col >= clen){
32293                 row++;
32294                 first = false;
32295             }
32296             while(row < rlen){
32297                 if(!first){
32298                     col = 0;
32299                 }
32300                 first = false;
32301                 while(col < clen){
32302                     if(fn.call(scope || this, row, col, cm) === true){
32303                         return [row, col];
32304                     }
32305                     col++;
32306                 }
32307                 row++;
32308             }
32309         }
32310         return null;
32311     },
32312
32313     // private
32314     getSelections : function(){
32315         return this.selModel.getSelections();
32316     },
32317
32318     /**
32319      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
32320      * but if manual update is required this method will initiate it.
32321      */
32322     autoSize : function(){
32323         if(this.rendered){
32324             this.view.layout();
32325             if(this.view.adjustForScroll){
32326                 this.view.adjustForScroll();
32327             }
32328         }
32329     },
32330
32331     /**
32332      * Returns the grid's underlying element.
32333      * @return {Element} The element
32334      */
32335     getGridEl : function(){
32336         return this.container;
32337     },
32338
32339     // private for compatibility, overridden by editor grid
32340     stopEditing : function(){},
32341
32342     /**
32343      * Returns the grid's SelectionModel.
32344      * @return {SelectionModel}
32345      */
32346     getSelectionModel : function(){
32347         if(!this.selModel){
32348             this.selModel = new Roo.grid.RowSelectionModel();
32349         }
32350         return this.selModel;
32351     },
32352
32353     /**
32354      * Returns the grid's DataSource.
32355      * @return {DataSource}
32356      */
32357     getDataSource : function(){
32358         return this.dataSource;
32359     },
32360
32361     /**
32362      * Returns the grid's ColumnModel.
32363      * @return {ColumnModel}
32364      */
32365     getColumnModel : function(){
32366         return this.colModel;
32367     },
32368
32369     /**
32370      * Returns the grid's GridView object.
32371      * @return {GridView}
32372      */
32373     getView : function(){
32374         if(!this.view){
32375             this.view = new Roo.grid.GridView(this.viewConfig);
32376         }
32377         return this.view;
32378     },
32379     /**
32380      * Called to get grid's drag proxy text, by default returns this.ddText.
32381      * @return {String}
32382      */
32383     getDragDropText : function(){
32384         var count = this.selModel.getCount();
32385         return String.format(this.ddText, count, count == 1 ? '' : 's');
32386     }
32387 });
32388 /**
32389  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
32390  * %0 is replaced with the number of selected rows.
32391  * @type String
32392  */
32393 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
32394  * Based on:
32395  * Ext JS Library 1.1.1
32396  * Copyright(c) 2006-2007, Ext JS, LLC.
32397  *
32398  * Originally Released Under LGPL - original licence link has changed is not relivant.
32399  *
32400  * Fork - LGPL
32401  * <script type="text/javascript">
32402  */
32403  
32404 Roo.grid.AbstractGridView = function(){
32405         this.grid = null;
32406         
32407         this.events = {
32408             "beforerowremoved" : true,
32409             "beforerowsinserted" : true,
32410             "beforerefresh" : true,
32411             "rowremoved" : true,
32412             "rowsinserted" : true,
32413             "rowupdated" : true,
32414             "refresh" : true
32415         };
32416     Roo.grid.AbstractGridView.superclass.constructor.call(this);
32417 };
32418
32419 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
32420     rowClass : "x-grid-row",
32421     cellClass : "x-grid-cell",
32422     tdClass : "x-grid-td",
32423     hdClass : "x-grid-hd",
32424     splitClass : "x-grid-hd-split",
32425     
32426     init: function(grid){
32427         this.grid = grid;
32428                 var cid = this.grid.getGridEl().id;
32429         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
32430         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
32431         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
32432         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
32433         },
32434         
32435     getColumnRenderers : function(){
32436         var renderers = [];
32437         var cm = this.grid.colModel;
32438         var colCount = cm.getColumnCount();
32439         for(var i = 0; i < colCount; i++){
32440             renderers[i] = cm.getRenderer(i);
32441         }
32442         return renderers;
32443     },
32444     
32445     getColumnIds : function(){
32446         var ids = [];
32447         var cm = this.grid.colModel;
32448         var colCount = cm.getColumnCount();
32449         for(var i = 0; i < colCount; i++){
32450             ids[i] = cm.getColumnId(i);
32451         }
32452         return ids;
32453     },
32454     
32455     getDataIndexes : function(){
32456         if(!this.indexMap){
32457             this.indexMap = this.buildIndexMap();
32458         }
32459         return this.indexMap.colToData;
32460     },
32461     
32462     getColumnIndexByDataIndex : function(dataIndex){
32463         if(!this.indexMap){
32464             this.indexMap = this.buildIndexMap();
32465         }
32466         return this.indexMap.dataToCol[dataIndex];
32467     },
32468     
32469     /**
32470      * Set a css style for a column dynamically. 
32471      * @param {Number} colIndex The index of the column
32472      * @param {String} name The css property name
32473      * @param {String} value The css value
32474      */
32475     setCSSStyle : function(colIndex, name, value){
32476         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
32477         Roo.util.CSS.updateRule(selector, name, value);
32478     },
32479     
32480     generateRules : function(cm){
32481         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
32482         Roo.util.CSS.removeStyleSheet(rulesId);
32483         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
32484             var cid = cm.getColumnId(i);
32485             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
32486                          this.tdSelector, cid, " {\n}\n",
32487                          this.hdSelector, cid, " {\n}\n",
32488                          this.splitSelector, cid, " {\n}\n");
32489         }
32490         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
32491     }
32492 });/*
32493  * Based on:
32494  * Ext JS Library 1.1.1
32495  * Copyright(c) 2006-2007, Ext JS, LLC.
32496  *
32497  * Originally Released Under LGPL - original licence link has changed is not relivant.
32498  *
32499  * Fork - LGPL
32500  * <script type="text/javascript">
32501  */
32502
32503 // private
32504 // This is a support class used internally by the Grid components
32505 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
32506     this.grid = grid;
32507     this.view = grid.getView();
32508     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
32509     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
32510     if(hd2){
32511         this.setHandleElId(Roo.id(hd));
32512         this.setOuterHandleElId(Roo.id(hd2));
32513     }
32514     this.scroll = false;
32515 };
32516 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
32517     maxDragWidth: 120,
32518     getDragData : function(e){
32519         var t = Roo.lib.Event.getTarget(e);
32520         var h = this.view.findHeaderCell(t);
32521         if(h){
32522             return {ddel: h.firstChild, header:h};
32523         }
32524         return false;
32525     },
32526
32527     onInitDrag : function(e){
32528         this.view.headersDisabled = true;
32529         var clone = this.dragData.ddel.cloneNode(true);
32530         clone.id = Roo.id();
32531         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
32532         this.proxy.update(clone);
32533         return true;
32534     },
32535
32536     afterValidDrop : function(){
32537         var v = this.view;
32538         setTimeout(function(){
32539             v.headersDisabled = false;
32540         }, 50);
32541     },
32542
32543     afterInvalidDrop : function(){
32544         var v = this.view;
32545         setTimeout(function(){
32546             v.headersDisabled = false;
32547         }, 50);
32548     }
32549 });
32550 /*
32551  * Based on:
32552  * Ext JS Library 1.1.1
32553  * Copyright(c) 2006-2007, Ext JS, LLC.
32554  *
32555  * Originally Released Under LGPL - original licence link has changed is not relivant.
32556  *
32557  * Fork - LGPL
32558  * <script type="text/javascript">
32559  */
32560 // private
32561 // This is a support class used internally by the Grid components
32562 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
32563     this.grid = grid;
32564     this.view = grid.getView();
32565     // split the proxies so they don't interfere with mouse events
32566     this.proxyTop = Roo.DomHelper.append(document.body, {
32567         cls:"col-move-top", html:"&#160;"
32568     }, true);
32569     this.proxyBottom = Roo.DomHelper.append(document.body, {
32570         cls:"col-move-bottom", html:"&#160;"
32571     }, true);
32572     this.proxyTop.hide = this.proxyBottom.hide = function(){
32573         this.setLeftTop(-100,-100);
32574         this.setStyle("visibility", "hidden");
32575     };
32576     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
32577     // temporarily disabled
32578     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
32579     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
32580 };
32581 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
32582     proxyOffsets : [-4, -9],
32583     fly: Roo.Element.fly,
32584
32585     getTargetFromEvent : function(e){
32586         var t = Roo.lib.Event.getTarget(e);
32587         var cindex = this.view.findCellIndex(t);
32588         if(cindex !== false){
32589             return this.view.getHeaderCell(cindex);
32590         }
32591         return null;
32592     },
32593
32594     nextVisible : function(h){
32595         var v = this.view, cm = this.grid.colModel;
32596         h = h.nextSibling;
32597         while(h){
32598             if(!cm.isHidden(v.getCellIndex(h))){
32599                 return h;
32600             }
32601             h = h.nextSibling;
32602         }
32603         return null;
32604     },
32605
32606     prevVisible : function(h){
32607         var v = this.view, cm = this.grid.colModel;
32608         h = h.prevSibling;
32609         while(h){
32610             if(!cm.isHidden(v.getCellIndex(h))){
32611                 return h;
32612             }
32613             h = h.prevSibling;
32614         }
32615         return null;
32616     },
32617
32618     positionIndicator : function(h, n, e){
32619         var x = Roo.lib.Event.getPageX(e);
32620         var r = Roo.lib.Dom.getRegion(n.firstChild);
32621         var px, pt, py = r.top + this.proxyOffsets[1];
32622         if((r.right - x) <= (r.right-r.left)/2){
32623             px = r.right+this.view.borderWidth;
32624             pt = "after";
32625         }else{
32626             px = r.left;
32627             pt = "before";
32628         }
32629         var oldIndex = this.view.getCellIndex(h);
32630         var newIndex = this.view.getCellIndex(n);
32631
32632         if(this.grid.colModel.isFixed(newIndex)){
32633             return false;
32634         }
32635
32636         var locked = this.grid.colModel.isLocked(newIndex);
32637
32638         if(pt == "after"){
32639             newIndex++;
32640         }
32641         if(oldIndex < newIndex){
32642             newIndex--;
32643         }
32644         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
32645             return false;
32646         }
32647         px +=  this.proxyOffsets[0];
32648         this.proxyTop.setLeftTop(px, py);
32649         this.proxyTop.show();
32650         if(!this.bottomOffset){
32651             this.bottomOffset = this.view.mainHd.getHeight();
32652         }
32653         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
32654         this.proxyBottom.show();
32655         return pt;
32656     },
32657
32658     onNodeEnter : function(n, dd, e, data){
32659         if(data.header != n){
32660             this.positionIndicator(data.header, n, e);
32661         }
32662     },
32663
32664     onNodeOver : function(n, dd, e, data){
32665         var result = false;
32666         if(data.header != n){
32667             result = this.positionIndicator(data.header, n, e);
32668         }
32669         if(!result){
32670             this.proxyTop.hide();
32671             this.proxyBottom.hide();
32672         }
32673         return result ? this.dropAllowed : this.dropNotAllowed;
32674     },
32675
32676     onNodeOut : function(n, dd, e, data){
32677         this.proxyTop.hide();
32678         this.proxyBottom.hide();
32679     },
32680
32681     onNodeDrop : function(n, dd, e, data){
32682         var h = data.header;
32683         if(h != n){
32684             var cm = this.grid.colModel;
32685             var x = Roo.lib.Event.getPageX(e);
32686             var r = Roo.lib.Dom.getRegion(n.firstChild);
32687             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
32688             var oldIndex = this.view.getCellIndex(h);
32689             var newIndex = this.view.getCellIndex(n);
32690             var locked = cm.isLocked(newIndex);
32691             if(pt == "after"){
32692                 newIndex++;
32693             }
32694             if(oldIndex < newIndex){
32695                 newIndex--;
32696             }
32697             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
32698                 return false;
32699             }
32700             cm.setLocked(oldIndex, locked, true);
32701             cm.moveColumn(oldIndex, newIndex);
32702             this.grid.fireEvent("columnmove", oldIndex, newIndex);
32703             return true;
32704         }
32705         return false;
32706     }
32707 });
32708 /*
32709  * Based on:
32710  * Ext JS Library 1.1.1
32711  * Copyright(c) 2006-2007, Ext JS, LLC.
32712  *
32713  * Originally Released Under LGPL - original licence link has changed is not relivant.
32714  *
32715  * Fork - LGPL
32716  * <script type="text/javascript">
32717  */
32718   
32719 /**
32720  * @class Roo.grid.GridView
32721  * @extends Roo.util.Observable
32722  *
32723  * @constructor
32724  * @param {Object} config
32725  */
32726 Roo.grid.GridView = function(config){
32727     Roo.grid.GridView.superclass.constructor.call(this);
32728     this.el = null;
32729
32730     Roo.apply(this, config);
32731 };
32732
32733 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
32734
32735     unselectable :  'unselectable="on"',
32736     unselectableCls :  'x-unselectable',
32737     
32738     
32739     rowClass : "x-grid-row",
32740
32741     cellClass : "x-grid-col",
32742
32743     tdClass : "x-grid-td",
32744
32745     hdClass : "x-grid-hd",
32746
32747     splitClass : "x-grid-split",
32748
32749     sortClasses : ["sort-asc", "sort-desc"],
32750
32751     enableMoveAnim : false,
32752
32753     hlColor: "C3DAF9",
32754
32755     dh : Roo.DomHelper,
32756
32757     fly : Roo.Element.fly,
32758
32759     css : Roo.util.CSS,
32760
32761     borderWidth: 1,
32762
32763     splitOffset: 3,
32764
32765     scrollIncrement : 22,
32766
32767     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
32768
32769     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
32770
32771     bind : function(ds, cm){
32772         if(this.ds){
32773             this.ds.un("load", this.onLoad, this);
32774             this.ds.un("datachanged", this.onDataChange, this);
32775             this.ds.un("add", this.onAdd, this);
32776             this.ds.un("remove", this.onRemove, this);
32777             this.ds.un("update", this.onUpdate, this);
32778             this.ds.un("clear", this.onClear, this);
32779         }
32780         if(ds){
32781             ds.on("load", this.onLoad, this);
32782             ds.on("datachanged", this.onDataChange, this);
32783             ds.on("add", this.onAdd, this);
32784             ds.on("remove", this.onRemove, this);
32785             ds.on("update", this.onUpdate, this);
32786             ds.on("clear", this.onClear, this);
32787         }
32788         this.ds = ds;
32789
32790         if(this.cm){
32791             this.cm.un("widthchange", this.onColWidthChange, this);
32792             this.cm.un("headerchange", this.onHeaderChange, this);
32793             this.cm.un("hiddenchange", this.onHiddenChange, this);
32794             this.cm.un("columnmoved", this.onColumnMove, this);
32795             this.cm.un("columnlockchange", this.onColumnLock, this);
32796         }
32797         if(cm){
32798             this.generateRules(cm);
32799             cm.on("widthchange", this.onColWidthChange, this);
32800             cm.on("headerchange", this.onHeaderChange, this);
32801             cm.on("hiddenchange", this.onHiddenChange, this);
32802             cm.on("columnmoved", this.onColumnMove, this);
32803             cm.on("columnlockchange", this.onColumnLock, this);
32804         }
32805         this.cm = cm;
32806     },
32807
32808     init: function(grid){
32809         Roo.grid.GridView.superclass.init.call(this, grid);
32810
32811         this.bind(grid.dataSource, grid.colModel);
32812
32813         grid.on("headerclick", this.handleHeaderClick, this);
32814
32815         if(grid.trackMouseOver){
32816             grid.on("mouseover", this.onRowOver, this);
32817             grid.on("mouseout", this.onRowOut, this);
32818         }
32819         grid.cancelTextSelection = function(){};
32820         this.gridId = grid.id;
32821
32822         var tpls = this.templates || {};
32823
32824         if(!tpls.master){
32825             tpls.master = new Roo.Template(
32826                '<div class="x-grid" hidefocus="true">',
32827                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
32828                   '<div class="x-grid-topbar"></div>',
32829                   '<div class="x-grid-scroller"><div></div></div>',
32830                   '<div class="x-grid-locked">',
32831                       '<div class="x-grid-header">{lockedHeader}</div>',
32832                       '<div class="x-grid-body">{lockedBody}</div>',
32833                   "</div>",
32834                   '<div class="x-grid-viewport">',
32835                       '<div class="x-grid-header">{header}</div>',
32836                       '<div class="x-grid-body">{body}</div>',
32837                   "</div>",
32838                   '<div class="x-grid-bottombar"></div>',
32839                  
32840                   '<div class="x-grid-resize-proxy">&#160;</div>',
32841                "</div>"
32842             );
32843             tpls.master.disableformats = true;
32844         }
32845
32846         if(!tpls.header){
32847             tpls.header = new Roo.Template(
32848                '<table border="0" cellspacing="0" cellpadding="0">',
32849                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
32850                "</table>{splits}"
32851             );
32852             tpls.header.disableformats = true;
32853         }
32854         tpls.header.compile();
32855
32856         if(!tpls.hcell){
32857             tpls.hcell = new Roo.Template(
32858                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
32859                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
32860                 "</div></td>"
32861              );
32862              tpls.hcell.disableFormats = true;
32863         }
32864         tpls.hcell.compile();
32865
32866         if(!tpls.hsplit){
32867             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
32868                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
32869             tpls.hsplit.disableFormats = true;
32870         }
32871         tpls.hsplit.compile();
32872
32873         if(!tpls.body){
32874             tpls.body = new Roo.Template(
32875                '<table border="0" cellspacing="0" cellpadding="0">',
32876                "<tbody>{rows}</tbody>",
32877                "</table>"
32878             );
32879             tpls.body.disableFormats = true;
32880         }
32881         tpls.body.compile();
32882
32883         if(!tpls.row){
32884             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
32885             tpls.row.disableFormats = true;
32886         }
32887         tpls.row.compile();
32888
32889         if(!tpls.cell){
32890             tpls.cell = new Roo.Template(
32891                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
32892                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
32893                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
32894                 "</td>"
32895             );
32896             tpls.cell.disableFormats = true;
32897         }
32898         tpls.cell.compile();
32899
32900         this.templates = tpls;
32901     },
32902
32903     // remap these for backwards compat
32904     onColWidthChange : function(){
32905         this.updateColumns.apply(this, arguments);
32906     },
32907     onHeaderChange : function(){
32908         this.updateHeaders.apply(this, arguments);
32909     }, 
32910     onHiddenChange : function(){
32911         this.handleHiddenChange.apply(this, arguments);
32912     },
32913     onColumnMove : function(){
32914         this.handleColumnMove.apply(this, arguments);
32915     },
32916     onColumnLock : function(){
32917         this.handleLockChange.apply(this, arguments);
32918     },
32919
32920     onDataChange : function(){
32921         this.refresh();
32922         this.updateHeaderSortState();
32923     },
32924
32925     onClear : function(){
32926         this.refresh();
32927     },
32928
32929     onUpdate : function(ds, record){
32930         this.refreshRow(record);
32931     },
32932
32933     refreshRow : function(record){
32934         var ds = this.ds, index;
32935         if(typeof record == 'number'){
32936             index = record;
32937             record = ds.getAt(index);
32938         }else{
32939             index = ds.indexOf(record);
32940         }
32941         this.insertRows(ds, index, index, true);
32942         this.onRemove(ds, record, index+1, true);
32943         this.syncRowHeights(index, index);
32944         this.layout();
32945         this.fireEvent("rowupdated", this, index, record);
32946     },
32947
32948     onAdd : function(ds, records, index){
32949         this.insertRows(ds, index, index + (records.length-1));
32950     },
32951
32952     onRemove : function(ds, record, index, isUpdate){
32953         if(isUpdate !== true){
32954             this.fireEvent("beforerowremoved", this, index, record);
32955         }
32956         var bt = this.getBodyTable(), lt = this.getLockedTable();
32957         if(bt.rows[index]){
32958             bt.firstChild.removeChild(bt.rows[index]);
32959         }
32960         if(lt.rows[index]){
32961             lt.firstChild.removeChild(lt.rows[index]);
32962         }
32963         if(isUpdate !== true){
32964             this.stripeRows(index);
32965             this.syncRowHeights(index, index);
32966             this.layout();
32967             this.fireEvent("rowremoved", this, index, record);
32968         }
32969     },
32970
32971     onLoad : function(){
32972         this.scrollToTop();
32973     },
32974
32975     /**
32976      * Scrolls the grid to the top
32977      */
32978     scrollToTop : function(){
32979         if(this.scroller){
32980             this.scroller.dom.scrollTop = 0;
32981             this.syncScroll();
32982         }
32983     },
32984
32985     /**
32986      * Gets a panel in the header of the grid that can be used for toolbars etc.
32987      * After modifying the contents of this panel a call to grid.autoSize() may be
32988      * required to register any changes in size.
32989      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
32990      * @return Roo.Element
32991      */
32992     getHeaderPanel : function(doShow){
32993         if(doShow){
32994             this.headerPanel.show();
32995         }
32996         return this.headerPanel;
32997     },
32998
32999     /**
33000      * Gets a panel in the footer of the grid that can be used for toolbars etc.
33001      * After modifying the contents of this panel a call to grid.autoSize() may be
33002      * required to register any changes in size.
33003      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
33004      * @return Roo.Element
33005      */
33006     getFooterPanel : function(doShow){
33007         if(doShow){
33008             this.footerPanel.show();
33009         }
33010         return this.footerPanel;
33011     },
33012
33013     initElements : function(){
33014         var E = Roo.Element;
33015         var el = this.grid.getGridEl().dom.firstChild;
33016         var cs = el.childNodes;
33017
33018         this.el = new E(el);
33019         
33020          this.focusEl = new E(el.firstChild);
33021         this.focusEl.swallowEvent("click", true);
33022         
33023         this.headerPanel = new E(cs[1]);
33024         this.headerPanel.enableDisplayMode("block");
33025
33026         this.scroller = new E(cs[2]);
33027         this.scrollSizer = new E(this.scroller.dom.firstChild);
33028
33029         this.lockedWrap = new E(cs[3]);
33030         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
33031         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
33032
33033         this.mainWrap = new E(cs[4]);
33034         this.mainHd = new E(this.mainWrap.dom.firstChild);
33035         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
33036
33037         this.footerPanel = new E(cs[5]);
33038         this.footerPanel.enableDisplayMode("block");
33039
33040         this.resizeProxy = new E(cs[6]);
33041
33042         this.headerSelector = String.format(
33043            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
33044            this.lockedHd.id, this.mainHd.id
33045         );
33046
33047         this.splitterSelector = String.format(
33048            '#{0} div.x-grid-split, #{1} div.x-grid-split',
33049            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
33050         );
33051     },
33052     idToCssName : function(s)
33053     {
33054         return s.replace(/[^a-z0-9]+/ig, '-');
33055     },
33056
33057     getHeaderCell : function(index){
33058         return Roo.DomQuery.select(this.headerSelector)[index];
33059     },
33060
33061     getHeaderCellMeasure : function(index){
33062         return this.getHeaderCell(index).firstChild;
33063     },
33064
33065     getHeaderCellText : function(index){
33066         return this.getHeaderCell(index).firstChild.firstChild;
33067     },
33068
33069     getLockedTable : function(){
33070         return this.lockedBody.dom.firstChild;
33071     },
33072
33073     getBodyTable : function(){
33074         return this.mainBody.dom.firstChild;
33075     },
33076
33077     getLockedRow : function(index){
33078         return this.getLockedTable().rows[index];
33079     },
33080
33081     getRow : function(index){
33082         return this.getBodyTable().rows[index];
33083     },
33084
33085     getRowComposite : function(index){
33086         if(!this.rowEl){
33087             this.rowEl = new Roo.CompositeElementLite();
33088         }
33089         var els = [], lrow, mrow;
33090         if(lrow = this.getLockedRow(index)){
33091             els.push(lrow);
33092         }
33093         if(mrow = this.getRow(index)){
33094             els.push(mrow);
33095         }
33096         this.rowEl.elements = els;
33097         return this.rowEl;
33098     },
33099     /**
33100      * Gets the 'td' of the cell
33101      * 
33102      * @param {Integer} rowIndex row to select
33103      * @param {Integer} colIndex column to select
33104      * 
33105      * @return {Object} 
33106      */
33107     getCell : function(rowIndex, colIndex){
33108         var locked = this.cm.getLockedCount();
33109         var source;
33110         if(colIndex < locked){
33111             source = this.lockedBody.dom.firstChild;
33112         }else{
33113             source = this.mainBody.dom.firstChild;
33114             colIndex -= locked;
33115         }
33116         return source.rows[rowIndex].childNodes[colIndex];
33117     },
33118
33119     getCellText : function(rowIndex, colIndex){
33120         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
33121     },
33122
33123     getCellBox : function(cell){
33124         var b = this.fly(cell).getBox();
33125         if(Roo.isOpera){ // opera fails to report the Y
33126             b.y = cell.offsetTop + this.mainBody.getY();
33127         }
33128         return b;
33129     },
33130
33131     getCellIndex : function(cell){
33132         var id = String(cell.className).match(this.cellRE);
33133         if(id){
33134             return parseInt(id[1], 10);
33135         }
33136         return 0;
33137     },
33138
33139     findHeaderIndex : function(n){
33140         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33141         return r ? this.getCellIndex(r) : false;
33142     },
33143
33144     findHeaderCell : function(n){
33145         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33146         return r ? r : false;
33147     },
33148
33149     findRowIndex : function(n){
33150         if(!n){
33151             return false;
33152         }
33153         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
33154         return r ? r.rowIndex : false;
33155     },
33156
33157     findCellIndex : function(node){
33158         var stop = this.el.dom;
33159         while(node && node != stop){
33160             if(this.findRE.test(node.className)){
33161                 return this.getCellIndex(node);
33162             }
33163             node = node.parentNode;
33164         }
33165         return false;
33166     },
33167
33168     getColumnId : function(index){
33169         return this.cm.getColumnId(index);
33170     },
33171
33172     getSplitters : function()
33173     {
33174         if(this.splitterSelector){
33175            return Roo.DomQuery.select(this.splitterSelector);
33176         }else{
33177             return null;
33178       }
33179     },
33180
33181     getSplitter : function(index){
33182         return this.getSplitters()[index];
33183     },
33184
33185     onRowOver : function(e, t){
33186         var row;
33187         if((row = this.findRowIndex(t)) !== false){
33188             this.getRowComposite(row).addClass("x-grid-row-over");
33189         }
33190     },
33191
33192     onRowOut : function(e, t){
33193         var row;
33194         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
33195             this.getRowComposite(row).removeClass("x-grid-row-over");
33196         }
33197     },
33198
33199     renderHeaders : function(){
33200         var cm = this.cm;
33201         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
33202         var cb = [], lb = [], sb = [], lsb = [], p = {};
33203         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33204             p.cellId = "x-grid-hd-0-" + i;
33205             p.splitId = "x-grid-csplit-0-" + i;
33206             p.id = cm.getColumnId(i);
33207             p.value = cm.getColumnHeader(i) || "";
33208             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
33209             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
33210             if(!cm.isLocked(i)){
33211                 cb[cb.length] = ct.apply(p);
33212                 sb[sb.length] = st.apply(p);
33213             }else{
33214                 lb[lb.length] = ct.apply(p);
33215                 lsb[lsb.length] = st.apply(p);
33216             }
33217         }
33218         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
33219                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
33220     },
33221
33222     updateHeaders : function(){
33223         var html = this.renderHeaders();
33224         this.lockedHd.update(html[0]);
33225         this.mainHd.update(html[1]);
33226     },
33227
33228     /**
33229      * Focuses the specified row.
33230      * @param {Number} row The row index
33231      */
33232     focusRow : function(row)
33233     {
33234         //Roo.log('GridView.focusRow');
33235         var x = this.scroller.dom.scrollLeft;
33236         this.focusCell(row, 0, false);
33237         this.scroller.dom.scrollLeft = x;
33238     },
33239
33240     /**
33241      * Focuses the specified cell.
33242      * @param {Number} row The row index
33243      * @param {Number} col The column index
33244      * @param {Boolean} hscroll false to disable horizontal scrolling
33245      */
33246     focusCell : function(row, col, hscroll)
33247     {
33248         //Roo.log('GridView.focusCell');
33249         var el = this.ensureVisible(row, col, hscroll);
33250         this.focusEl.alignTo(el, "tl-tl");
33251         if(Roo.isGecko){
33252             this.focusEl.focus();
33253         }else{
33254             this.focusEl.focus.defer(1, this.focusEl);
33255         }
33256     },
33257
33258     /**
33259      * Scrolls the specified cell into view
33260      * @param {Number} row The row index
33261      * @param {Number} col The column index
33262      * @param {Boolean} hscroll false to disable horizontal scrolling
33263      */
33264     ensureVisible : function(row, col, hscroll)
33265     {
33266         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
33267         //return null; //disable for testing.
33268         if(typeof row != "number"){
33269             row = row.rowIndex;
33270         }
33271         if(row < 0 && row >= this.ds.getCount()){
33272             return  null;
33273         }
33274         col = (col !== undefined ? col : 0);
33275         var cm = this.grid.colModel;
33276         while(cm.isHidden(col)){
33277             col++;
33278         }
33279
33280         var el = this.getCell(row, col);
33281         if(!el){
33282             return null;
33283         }
33284         var c = this.scroller.dom;
33285
33286         var ctop = parseInt(el.offsetTop, 10);
33287         var cleft = parseInt(el.offsetLeft, 10);
33288         var cbot = ctop + el.offsetHeight;
33289         var cright = cleft + el.offsetWidth;
33290         
33291         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
33292         var stop = parseInt(c.scrollTop, 10);
33293         var sleft = parseInt(c.scrollLeft, 10);
33294         var sbot = stop + ch;
33295         var sright = sleft + c.clientWidth;
33296         /*
33297         Roo.log('GridView.ensureVisible:' +
33298                 ' ctop:' + ctop +
33299                 ' c.clientHeight:' + c.clientHeight +
33300                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
33301                 ' stop:' + stop +
33302                 ' cbot:' + cbot +
33303                 ' sbot:' + sbot +
33304                 ' ch:' + ch  
33305                 );
33306         */
33307         if(ctop < stop){
33308              c.scrollTop = ctop;
33309             //Roo.log("set scrolltop to ctop DISABLE?");
33310         }else if(cbot > sbot){
33311             //Roo.log("set scrolltop to cbot-ch");
33312             c.scrollTop = cbot-ch;
33313         }
33314         
33315         if(hscroll !== false){
33316             if(cleft < sleft){
33317                 c.scrollLeft = cleft;
33318             }else if(cright > sright){
33319                 c.scrollLeft = cright-c.clientWidth;
33320             }
33321         }
33322          
33323         return el;
33324     },
33325
33326     updateColumns : function(){
33327         this.grid.stopEditing();
33328         var cm = this.grid.colModel, colIds = this.getColumnIds();
33329         //var totalWidth = cm.getTotalWidth();
33330         var pos = 0;
33331         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33332             //if(cm.isHidden(i)) continue;
33333             var w = cm.getColumnWidth(i);
33334             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33335             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33336         }
33337         this.updateSplitters();
33338     },
33339
33340     generateRules : function(cm){
33341         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
33342         Roo.util.CSS.removeStyleSheet(rulesId);
33343         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33344             var cid = cm.getColumnId(i);
33345             var align = '';
33346             if(cm.config[i].align){
33347                 align = 'text-align:'+cm.config[i].align+';';
33348             }
33349             var hidden = '';
33350             if(cm.isHidden(i)){
33351                 hidden = 'display:none;';
33352             }
33353             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
33354             ruleBuf.push(
33355                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
33356                     this.hdSelector, cid, " {\n", align, width, "}\n",
33357                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
33358                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
33359         }
33360         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33361     },
33362
33363     updateSplitters : function(){
33364         var cm = this.cm, s = this.getSplitters();
33365         if(s){ // splitters not created yet
33366             var pos = 0, locked = true;
33367             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33368                 if(cm.isHidden(i)) {
33369                     continue;
33370                 }
33371                 var w = cm.getColumnWidth(i); // make sure it's a number
33372                 if(!cm.isLocked(i) && locked){
33373                     pos = 0;
33374                     locked = false;
33375                 }
33376                 pos += w;
33377                 s[i].style.left = (pos-this.splitOffset) + "px";
33378             }
33379         }
33380     },
33381
33382     handleHiddenChange : function(colModel, colIndex, hidden){
33383         if(hidden){
33384             this.hideColumn(colIndex);
33385         }else{
33386             this.unhideColumn(colIndex);
33387         }
33388     },
33389
33390     hideColumn : function(colIndex){
33391         var cid = this.getColumnId(colIndex);
33392         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
33393         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
33394         if(Roo.isSafari){
33395             this.updateHeaders();
33396         }
33397         this.updateSplitters();
33398         this.layout();
33399     },
33400
33401     unhideColumn : function(colIndex){
33402         var cid = this.getColumnId(colIndex);
33403         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
33404         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
33405
33406         if(Roo.isSafari){
33407             this.updateHeaders();
33408         }
33409         this.updateSplitters();
33410         this.layout();
33411     },
33412
33413     insertRows : function(dm, firstRow, lastRow, isUpdate){
33414         if(firstRow == 0 && lastRow == dm.getCount()-1){
33415             this.refresh();
33416         }else{
33417             if(!isUpdate){
33418                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
33419             }
33420             var s = this.getScrollState();
33421             var markup = this.renderRows(firstRow, lastRow);
33422             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
33423             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
33424             this.restoreScroll(s);
33425             if(!isUpdate){
33426                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
33427                 this.syncRowHeights(firstRow, lastRow);
33428                 this.stripeRows(firstRow);
33429                 this.layout();
33430             }
33431         }
33432     },
33433
33434     bufferRows : function(markup, target, index){
33435         var before = null, trows = target.rows, tbody = target.tBodies[0];
33436         if(index < trows.length){
33437             before = trows[index];
33438         }
33439         var b = document.createElement("div");
33440         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
33441         var rows = b.firstChild.rows;
33442         for(var i = 0, len = rows.length; i < len; i++){
33443             if(before){
33444                 tbody.insertBefore(rows[0], before);
33445             }else{
33446                 tbody.appendChild(rows[0]);
33447             }
33448         }
33449         b.innerHTML = "";
33450         b = null;
33451     },
33452
33453     deleteRows : function(dm, firstRow, lastRow){
33454         if(dm.getRowCount()<1){
33455             this.fireEvent("beforerefresh", this);
33456             this.mainBody.update("");
33457             this.lockedBody.update("");
33458             this.fireEvent("refresh", this);
33459         }else{
33460             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
33461             var bt = this.getBodyTable();
33462             var tbody = bt.firstChild;
33463             var rows = bt.rows;
33464             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
33465                 tbody.removeChild(rows[firstRow]);
33466             }
33467             this.stripeRows(firstRow);
33468             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
33469         }
33470     },
33471
33472     updateRows : function(dataSource, firstRow, lastRow){
33473         var s = this.getScrollState();
33474         this.refresh();
33475         this.restoreScroll(s);
33476     },
33477
33478     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
33479         if(!noRefresh){
33480            this.refresh();
33481         }
33482         this.updateHeaderSortState();
33483     },
33484
33485     getScrollState : function(){
33486         
33487         var sb = this.scroller.dom;
33488         return {left: sb.scrollLeft, top: sb.scrollTop};
33489     },
33490
33491     stripeRows : function(startRow){
33492         if(!this.grid.stripeRows || this.ds.getCount() < 1){
33493             return;
33494         }
33495         startRow = startRow || 0;
33496         var rows = this.getBodyTable().rows;
33497         var lrows = this.getLockedTable().rows;
33498         var cls = ' x-grid-row-alt ';
33499         for(var i = startRow, len = rows.length; i < len; i++){
33500             var row = rows[i], lrow = lrows[i];
33501             var isAlt = ((i+1) % 2 == 0);
33502             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
33503             if(isAlt == hasAlt){
33504                 continue;
33505             }
33506             if(isAlt){
33507                 row.className += " x-grid-row-alt";
33508             }else{
33509                 row.className = row.className.replace("x-grid-row-alt", "");
33510             }
33511             if(lrow){
33512                 lrow.className = row.className;
33513             }
33514         }
33515     },
33516
33517     restoreScroll : function(state){
33518         //Roo.log('GridView.restoreScroll');
33519         var sb = this.scroller.dom;
33520         sb.scrollLeft = state.left;
33521         sb.scrollTop = state.top;
33522         this.syncScroll();
33523     },
33524
33525     syncScroll : function(){
33526         //Roo.log('GridView.syncScroll');
33527         var sb = this.scroller.dom;
33528         var sh = this.mainHd.dom;
33529         var bs = this.mainBody.dom;
33530         var lv = this.lockedBody.dom;
33531         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
33532         lv.scrollTop = bs.scrollTop = sb.scrollTop;
33533     },
33534
33535     handleScroll : function(e){
33536         this.syncScroll();
33537         var sb = this.scroller.dom;
33538         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
33539         e.stopEvent();
33540     },
33541
33542     handleWheel : function(e){
33543         var d = e.getWheelDelta();
33544         this.scroller.dom.scrollTop -= d*22;
33545         // set this here to prevent jumpy scrolling on large tables
33546         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
33547         e.stopEvent();
33548     },
33549
33550     renderRows : function(startRow, endRow){
33551         // pull in all the crap needed to render rows
33552         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
33553         var colCount = cm.getColumnCount();
33554
33555         if(ds.getCount() < 1){
33556             return ["", ""];
33557         }
33558
33559         // build a map for all the columns
33560         var cs = [];
33561         for(var i = 0; i < colCount; i++){
33562             var name = cm.getDataIndex(i);
33563             cs[i] = {
33564                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
33565                 renderer : cm.getRenderer(i),
33566                 id : cm.getColumnId(i),
33567                 locked : cm.isLocked(i),
33568                 has_editor : cm.isCellEditable(i)
33569             };
33570         }
33571
33572         startRow = startRow || 0;
33573         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
33574
33575         // records to render
33576         var rs = ds.getRange(startRow, endRow);
33577
33578         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
33579     },
33580
33581     // As much as I hate to duplicate code, this was branched because FireFox really hates
33582     // [].join("") on strings. The performance difference was substantial enough to
33583     // branch this function
33584     doRender : Roo.isGecko ?
33585             function(cs, rs, ds, startRow, colCount, stripe){
33586                 var ts = this.templates, ct = ts.cell, rt = ts.row;
33587                 // buffers
33588                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
33589                 
33590                 var hasListener = this.grid.hasListener('rowclass');
33591                 var rowcfg = {};
33592                 for(var j = 0, len = rs.length; j < len; j++){
33593                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
33594                     for(var i = 0; i < colCount; i++){
33595                         c = cs[i];
33596                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
33597                         p.id = c.id;
33598                         p.css = p.attr = "";
33599                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
33600                         if(p.value == undefined || p.value === "") {
33601                             p.value = "&#160;";
33602                         }
33603                         if(c.has_editor){
33604                             p.css += ' x-grid-editable-cell';
33605                         }
33606                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
33607                             p.css +=  ' x-grid-dirty-cell';
33608                         }
33609                         var markup = ct.apply(p);
33610                         if(!c.locked){
33611                             cb+= markup;
33612                         }else{
33613                             lcb+= markup;
33614                         }
33615                     }
33616                     var alt = [];
33617                     if(stripe && ((rowIndex+1) % 2 == 0)){
33618                         alt.push("x-grid-row-alt")
33619                     }
33620                     if(r.dirty){
33621                         alt.push(  " x-grid-dirty-row");
33622                     }
33623                     rp.cells = lcb;
33624                     if(this.getRowClass){
33625                         alt.push(this.getRowClass(r, rowIndex));
33626                     }
33627                     if (hasListener) {
33628                         rowcfg = {
33629                              
33630                             record: r,
33631                             rowIndex : rowIndex,
33632                             rowClass : ''
33633                         };
33634                         this.grid.fireEvent('rowclass', this, rowcfg);
33635                         alt.push(rowcfg.rowClass);
33636                     }
33637                     rp.alt = alt.join(" ");
33638                     lbuf+= rt.apply(rp);
33639                     rp.cells = cb;
33640                     buf+=  rt.apply(rp);
33641                 }
33642                 return [lbuf, buf];
33643             } :
33644             function(cs, rs, ds, startRow, colCount, stripe){
33645                 var ts = this.templates, ct = ts.cell, rt = ts.row;
33646                 // buffers
33647                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
33648                 var hasListener = this.grid.hasListener('rowclass');
33649  
33650                 var rowcfg = {};
33651                 for(var j = 0, len = rs.length; j < len; j++){
33652                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
33653                     for(var i = 0; i < colCount; i++){
33654                         c = cs[i];
33655                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
33656                         p.id = c.id;
33657                         p.css = p.attr = "";
33658                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
33659                         if(p.value == undefined || p.value === "") {
33660                             p.value = "&#160;";
33661                         }
33662                         //Roo.log(c);
33663                          if(c.has_editor){
33664                             p.css += ' x-grid-editable-cell';
33665                         }
33666                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
33667                             p.css += ' x-grid-dirty-cell' 
33668                         }
33669                         
33670                         var markup = ct.apply(p);
33671                         if(!c.locked){
33672                             cb[cb.length] = markup;
33673                         }else{
33674                             lcb[lcb.length] = markup;
33675                         }
33676                     }
33677                     var alt = [];
33678                     if(stripe && ((rowIndex+1) % 2 == 0)){
33679                         alt.push( "x-grid-row-alt");
33680                     }
33681                     if(r.dirty){
33682                         alt.push(" x-grid-dirty-row");
33683                     }
33684                     rp.cells = lcb;
33685                     if(this.getRowClass){
33686                         alt.push( this.getRowClass(r, rowIndex));
33687                     }
33688                     if (hasListener) {
33689                         rowcfg = {
33690                              
33691                             record: r,
33692                             rowIndex : rowIndex,
33693                             rowClass : ''
33694                         };
33695                         this.grid.fireEvent('rowclass', this, rowcfg);
33696                         alt.push(rowcfg.rowClass);
33697                     }
33698                     
33699                     rp.alt = alt.join(" ");
33700                     rp.cells = lcb.join("");
33701                     lbuf[lbuf.length] = rt.apply(rp);
33702                     rp.cells = cb.join("");
33703                     buf[buf.length] =  rt.apply(rp);
33704                 }
33705                 return [lbuf.join(""), buf.join("")];
33706             },
33707
33708     renderBody : function(){
33709         var markup = this.renderRows();
33710         var bt = this.templates.body;
33711         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
33712     },
33713
33714     /**
33715      * Refreshes the grid
33716      * @param {Boolean} headersToo
33717      */
33718     refresh : function(headersToo){
33719         this.fireEvent("beforerefresh", this);
33720         this.grid.stopEditing();
33721         var result = this.renderBody();
33722         this.lockedBody.update(result[0]);
33723         this.mainBody.update(result[1]);
33724         if(headersToo === true){
33725             this.updateHeaders();
33726             this.updateColumns();
33727             this.updateSplitters();
33728             this.updateHeaderSortState();
33729         }
33730         this.syncRowHeights();
33731         this.layout();
33732         this.fireEvent("refresh", this);
33733     },
33734
33735     handleColumnMove : function(cm, oldIndex, newIndex){
33736         this.indexMap = null;
33737         var s = this.getScrollState();
33738         this.refresh(true);
33739         this.restoreScroll(s);
33740         this.afterMove(newIndex);
33741     },
33742
33743     afterMove : function(colIndex){
33744         if(this.enableMoveAnim && Roo.enableFx){
33745             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
33746         }
33747         // if multisort - fix sortOrder, and reload..
33748         if (this.grid.dataSource.multiSort) {
33749             // the we can call sort again..
33750             var dm = this.grid.dataSource;
33751             var cm = this.grid.colModel;
33752             var so = [];
33753             for(var i = 0; i < cm.config.length; i++ ) {
33754                 
33755                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
33756                     continue; // dont' bother, it's not in sort list or being set.
33757                 }
33758                 
33759                 so.push(cm.config[i].dataIndex);
33760             };
33761             dm.sortOrder = so;
33762             dm.load(dm.lastOptions);
33763             
33764             
33765         }
33766         
33767     },
33768
33769     updateCell : function(dm, rowIndex, dataIndex){
33770         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
33771         if(typeof colIndex == "undefined"){ // not present in grid
33772             return;
33773         }
33774         var cm = this.grid.colModel;
33775         var cell = this.getCell(rowIndex, colIndex);
33776         var cellText = this.getCellText(rowIndex, colIndex);
33777
33778         var p = {
33779             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
33780             id : cm.getColumnId(colIndex),
33781             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
33782         };
33783         var renderer = cm.getRenderer(colIndex);
33784         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
33785         if(typeof val == "undefined" || val === "") {
33786             val = "&#160;";
33787         }
33788         cellText.innerHTML = val;
33789         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
33790         this.syncRowHeights(rowIndex, rowIndex);
33791     },
33792
33793     calcColumnWidth : function(colIndex, maxRowsToMeasure){
33794         var maxWidth = 0;
33795         if(this.grid.autoSizeHeaders){
33796             var h = this.getHeaderCellMeasure(colIndex);
33797             maxWidth = Math.max(maxWidth, h.scrollWidth);
33798         }
33799         var tb, index;
33800         if(this.cm.isLocked(colIndex)){
33801             tb = this.getLockedTable();
33802             index = colIndex;
33803         }else{
33804             tb = this.getBodyTable();
33805             index = colIndex - this.cm.getLockedCount();
33806         }
33807         if(tb && tb.rows){
33808             var rows = tb.rows;
33809             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
33810             for(var i = 0; i < stopIndex; i++){
33811                 var cell = rows[i].childNodes[index].firstChild;
33812                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
33813             }
33814         }
33815         return maxWidth + /*margin for error in IE*/ 5;
33816     },
33817     /**
33818      * Autofit a column to its content.
33819      * @param {Number} colIndex
33820      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
33821      */
33822      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
33823          if(this.cm.isHidden(colIndex)){
33824              return; // can't calc a hidden column
33825          }
33826         if(forceMinSize){
33827             var cid = this.cm.getColumnId(colIndex);
33828             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
33829            if(this.grid.autoSizeHeaders){
33830                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
33831            }
33832         }
33833         var newWidth = this.calcColumnWidth(colIndex);
33834         this.cm.setColumnWidth(colIndex,
33835             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
33836         if(!suppressEvent){
33837             this.grid.fireEvent("columnresize", colIndex, newWidth);
33838         }
33839     },
33840
33841     /**
33842      * Autofits all columns to their content and then expands to fit any extra space in the grid
33843      */
33844      autoSizeColumns : function(){
33845         var cm = this.grid.colModel;
33846         var colCount = cm.getColumnCount();
33847         for(var i = 0; i < colCount; i++){
33848             this.autoSizeColumn(i, true, true);
33849         }
33850         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
33851             this.fitColumns();
33852         }else{
33853             this.updateColumns();
33854             this.layout();
33855         }
33856     },
33857
33858     /**
33859      * Autofits all columns to the grid's width proportionate with their current size
33860      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
33861      */
33862     fitColumns : function(reserveScrollSpace){
33863         var cm = this.grid.colModel;
33864         var colCount = cm.getColumnCount();
33865         var cols = [];
33866         var width = 0;
33867         var i, w;
33868         for (i = 0; i < colCount; i++){
33869             if(!cm.isHidden(i) && !cm.isFixed(i)){
33870                 w = cm.getColumnWidth(i);
33871                 cols.push(i);
33872                 cols.push(w);
33873                 width += w;
33874             }
33875         }
33876         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
33877         if(reserveScrollSpace){
33878             avail -= 17;
33879         }
33880         var frac = (avail - cm.getTotalWidth())/width;
33881         while (cols.length){
33882             w = cols.pop();
33883             i = cols.pop();
33884             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
33885         }
33886         this.updateColumns();
33887         this.layout();
33888     },
33889
33890     onRowSelect : function(rowIndex){
33891         var row = this.getRowComposite(rowIndex);
33892         row.addClass("x-grid-row-selected");
33893     },
33894
33895     onRowDeselect : function(rowIndex){
33896         var row = this.getRowComposite(rowIndex);
33897         row.removeClass("x-grid-row-selected");
33898     },
33899
33900     onCellSelect : function(row, col){
33901         var cell = this.getCell(row, col);
33902         if(cell){
33903             Roo.fly(cell).addClass("x-grid-cell-selected");
33904         }
33905     },
33906
33907     onCellDeselect : function(row, col){
33908         var cell = this.getCell(row, col);
33909         if(cell){
33910             Roo.fly(cell).removeClass("x-grid-cell-selected");
33911         }
33912     },
33913
33914     updateHeaderSortState : function(){
33915         
33916         // sort state can be single { field: xxx, direction : yyy}
33917         // or   { xxx=>ASC , yyy : DESC ..... }
33918         
33919         var mstate = {};
33920         if (!this.ds.multiSort) { 
33921             var state = this.ds.getSortState();
33922             if(!state){
33923                 return;
33924             }
33925             mstate[state.field] = state.direction;
33926             // FIXME... - this is not used here.. but might be elsewhere..
33927             this.sortState = state;
33928             
33929         } else {
33930             mstate = this.ds.sortToggle;
33931         }
33932         //remove existing sort classes..
33933         
33934         var sc = this.sortClasses;
33935         var hds = this.el.select(this.headerSelector).removeClass(sc);
33936         
33937         for(var f in mstate) {
33938         
33939             var sortColumn = this.cm.findColumnIndex(f);
33940             
33941             if(sortColumn != -1){
33942                 var sortDir = mstate[f];        
33943                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
33944             }
33945         }
33946         
33947          
33948         
33949     },
33950
33951
33952     handleHeaderClick : function(g, index,e){
33953         
33954         Roo.log("header click");
33955         
33956         if (Roo.isTouch) {
33957             // touch events on header are handled by context
33958             this.handleHdCtx(g,index,e);
33959             return;
33960         }
33961         
33962         
33963         if(this.headersDisabled){
33964             return;
33965         }
33966         var dm = g.dataSource, cm = g.colModel;
33967         if(!cm.isSortable(index)){
33968             return;
33969         }
33970         g.stopEditing();
33971         
33972         if (dm.multiSort) {
33973             // update the sortOrder
33974             var so = [];
33975             for(var i = 0; i < cm.config.length; i++ ) {
33976                 
33977                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
33978                     continue; // dont' bother, it's not in sort list or being set.
33979                 }
33980                 
33981                 so.push(cm.config[i].dataIndex);
33982             };
33983             dm.sortOrder = so;
33984         }
33985         
33986         
33987         dm.sort(cm.getDataIndex(index));
33988     },
33989
33990
33991     destroy : function(){
33992         if(this.colMenu){
33993             this.colMenu.removeAll();
33994             Roo.menu.MenuMgr.unregister(this.colMenu);
33995             this.colMenu.getEl().remove();
33996             delete this.colMenu;
33997         }
33998         if(this.hmenu){
33999             this.hmenu.removeAll();
34000             Roo.menu.MenuMgr.unregister(this.hmenu);
34001             this.hmenu.getEl().remove();
34002             delete this.hmenu;
34003         }
34004         if(this.grid.enableColumnMove){
34005             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34006             if(dds){
34007                 for(var dd in dds){
34008                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
34009                         var elid = dds[dd].dragElId;
34010                         dds[dd].unreg();
34011                         Roo.get(elid).remove();
34012                     } else if(dds[dd].config.isTarget){
34013                         dds[dd].proxyTop.remove();
34014                         dds[dd].proxyBottom.remove();
34015                         dds[dd].unreg();
34016                     }
34017                     if(Roo.dd.DDM.locationCache[dd]){
34018                         delete Roo.dd.DDM.locationCache[dd];
34019                     }
34020                 }
34021                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34022             }
34023         }
34024         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
34025         this.bind(null, null);
34026         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
34027     },
34028
34029     handleLockChange : function(){
34030         this.refresh(true);
34031     },
34032
34033     onDenyColumnLock : function(){
34034
34035     },
34036
34037     onDenyColumnHide : function(){
34038
34039     },
34040
34041     handleHdMenuClick : function(item){
34042         var index = this.hdCtxIndex;
34043         var cm = this.cm, ds = this.ds;
34044         switch(item.id){
34045             case "asc":
34046                 ds.sort(cm.getDataIndex(index), "ASC");
34047                 break;
34048             case "desc":
34049                 ds.sort(cm.getDataIndex(index), "DESC");
34050                 break;
34051             case "lock":
34052                 var lc = cm.getLockedCount();
34053                 if(cm.getColumnCount(true) <= lc+1){
34054                     this.onDenyColumnLock();
34055                     return;
34056                 }
34057                 if(lc != index){
34058                     cm.setLocked(index, true, true);
34059                     cm.moveColumn(index, lc);
34060                     this.grid.fireEvent("columnmove", index, lc);
34061                 }else{
34062                     cm.setLocked(index, true);
34063                 }
34064             break;
34065             case "unlock":
34066                 var lc = cm.getLockedCount();
34067                 if((lc-1) != index){
34068                     cm.setLocked(index, false, true);
34069                     cm.moveColumn(index, lc-1);
34070                     this.grid.fireEvent("columnmove", index, lc-1);
34071                 }else{
34072                     cm.setLocked(index, false);
34073                 }
34074             break;
34075             case 'wider': // used to expand cols on touch..
34076             case 'narrow':
34077                 var cw = cm.getColumnWidth(index);
34078                 cw += (item.id == 'wider' ? 1 : -1) * 50;
34079                 cw = Math.max(0, cw);
34080                 cw = Math.min(cw,4000);
34081                 cm.setColumnWidth(index, cw);
34082                 break;
34083                 
34084             default:
34085                 index = cm.getIndexById(item.id.substr(4));
34086                 if(index != -1){
34087                     if(item.checked && cm.getColumnCount(true) <= 1){
34088                         this.onDenyColumnHide();
34089                         return false;
34090                     }
34091                     cm.setHidden(index, item.checked);
34092                 }
34093         }
34094         return true;
34095     },
34096
34097     beforeColMenuShow : function(){
34098         var cm = this.cm,  colCount = cm.getColumnCount();
34099         this.colMenu.removeAll();
34100         for(var i = 0; i < colCount; i++){
34101             this.colMenu.add(new Roo.menu.CheckItem({
34102                 id: "col-"+cm.getColumnId(i),
34103                 text: cm.getColumnHeader(i),
34104                 checked: !cm.isHidden(i),
34105                 hideOnClick:false
34106             }));
34107         }
34108     },
34109
34110     handleHdCtx : function(g, index, e){
34111         e.stopEvent();
34112         var hd = this.getHeaderCell(index);
34113         this.hdCtxIndex = index;
34114         var ms = this.hmenu.items, cm = this.cm;
34115         ms.get("asc").setDisabled(!cm.isSortable(index));
34116         ms.get("desc").setDisabled(!cm.isSortable(index));
34117         if(this.grid.enableColLock !== false){
34118             ms.get("lock").setDisabled(cm.isLocked(index));
34119             ms.get("unlock").setDisabled(!cm.isLocked(index));
34120         }
34121         this.hmenu.show(hd, "tl-bl");
34122     },
34123
34124     handleHdOver : function(e){
34125         var hd = this.findHeaderCell(e.getTarget());
34126         if(hd && !this.headersDisabled){
34127             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
34128                this.fly(hd).addClass("x-grid-hd-over");
34129             }
34130         }
34131     },
34132
34133     handleHdOut : function(e){
34134         var hd = this.findHeaderCell(e.getTarget());
34135         if(hd){
34136             this.fly(hd).removeClass("x-grid-hd-over");
34137         }
34138     },
34139
34140     handleSplitDblClick : function(e, t){
34141         var i = this.getCellIndex(t);
34142         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
34143             this.autoSizeColumn(i, true);
34144             this.layout();
34145         }
34146     },
34147
34148     render : function(){
34149
34150         var cm = this.cm;
34151         var colCount = cm.getColumnCount();
34152
34153         if(this.grid.monitorWindowResize === true){
34154             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34155         }
34156         var header = this.renderHeaders();
34157         var body = this.templates.body.apply({rows:""});
34158         var html = this.templates.master.apply({
34159             lockedBody: body,
34160             body: body,
34161             lockedHeader: header[0],
34162             header: header[1]
34163         });
34164
34165         //this.updateColumns();
34166
34167         this.grid.getGridEl().dom.innerHTML = html;
34168
34169         this.initElements();
34170         
34171         // a kludge to fix the random scolling effect in webkit
34172         this.el.on("scroll", function() {
34173             this.el.dom.scrollTop=0; // hopefully not recursive..
34174         },this);
34175
34176         this.scroller.on("scroll", this.handleScroll, this);
34177         this.lockedBody.on("mousewheel", this.handleWheel, this);
34178         this.mainBody.on("mousewheel", this.handleWheel, this);
34179
34180         this.mainHd.on("mouseover", this.handleHdOver, this);
34181         this.mainHd.on("mouseout", this.handleHdOut, this);
34182         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
34183                 {delegate: "."+this.splitClass});
34184
34185         this.lockedHd.on("mouseover", this.handleHdOver, this);
34186         this.lockedHd.on("mouseout", this.handleHdOut, this);
34187         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
34188                 {delegate: "."+this.splitClass});
34189
34190         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
34191             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34192         }
34193
34194         this.updateSplitters();
34195
34196         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
34197             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34198             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34199         }
34200
34201         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
34202             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
34203             this.hmenu.add(
34204                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
34205                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
34206             );
34207             if(this.grid.enableColLock !== false){
34208                 this.hmenu.add('-',
34209                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
34210                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
34211                 );
34212             }
34213             if (Roo.isTouch) {
34214                  this.hmenu.add('-',
34215                     {id:"wider", text: this.columnsWiderText},
34216                     {id:"narrow", text: this.columnsNarrowText }
34217                 );
34218                 
34219                  
34220             }
34221             
34222             if(this.grid.enableColumnHide !== false){
34223
34224                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
34225                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
34226                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
34227
34228                 this.hmenu.add('-',
34229                     {id:"columns", text: this.columnsText, menu: this.colMenu}
34230                 );
34231             }
34232             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
34233
34234             this.grid.on("headercontextmenu", this.handleHdCtx, this);
34235         }
34236
34237         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
34238             this.dd = new Roo.grid.GridDragZone(this.grid, {
34239                 ddGroup : this.grid.ddGroup || 'GridDD'
34240             });
34241             
34242         }
34243
34244         /*
34245         for(var i = 0; i < colCount; i++){
34246             if(cm.isHidden(i)){
34247                 this.hideColumn(i);
34248             }
34249             if(cm.config[i].align){
34250                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
34251                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
34252             }
34253         }*/
34254         
34255         this.updateHeaderSortState();
34256
34257         this.beforeInitialResize();
34258         this.layout(true);
34259
34260         // two part rendering gives faster view to the user
34261         this.renderPhase2.defer(1, this);
34262     },
34263
34264     renderPhase2 : function(){
34265         // render the rows now
34266         this.refresh();
34267         if(this.grid.autoSizeColumns){
34268             this.autoSizeColumns();
34269         }
34270     },
34271
34272     beforeInitialResize : function(){
34273
34274     },
34275
34276     onColumnSplitterMoved : function(i, w){
34277         this.userResized = true;
34278         var cm = this.grid.colModel;
34279         cm.setColumnWidth(i, w, true);
34280         var cid = cm.getColumnId(i);
34281         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34282         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34283         this.updateSplitters();
34284         this.layout();
34285         this.grid.fireEvent("columnresize", i, w);
34286     },
34287
34288     syncRowHeights : function(startIndex, endIndex){
34289         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
34290             startIndex = startIndex || 0;
34291             var mrows = this.getBodyTable().rows;
34292             var lrows = this.getLockedTable().rows;
34293             var len = mrows.length-1;
34294             endIndex = Math.min(endIndex || len, len);
34295             for(var i = startIndex; i <= endIndex; i++){
34296                 var m = mrows[i], l = lrows[i];
34297                 var h = Math.max(m.offsetHeight, l.offsetHeight);
34298                 m.style.height = l.style.height = h + "px";
34299             }
34300         }
34301     },
34302
34303     layout : function(initialRender, is2ndPass){
34304         var g = this.grid;
34305         var auto = g.autoHeight;
34306         var scrollOffset = 16;
34307         var c = g.getGridEl(), cm = this.cm,
34308                 expandCol = g.autoExpandColumn,
34309                 gv = this;
34310         //c.beginMeasure();
34311
34312         if(!c.dom.offsetWidth){ // display:none?
34313             if(initialRender){
34314                 this.lockedWrap.show();
34315                 this.mainWrap.show();
34316             }
34317             return;
34318         }
34319
34320         var hasLock = this.cm.isLocked(0);
34321
34322         var tbh = this.headerPanel.getHeight();
34323         var bbh = this.footerPanel.getHeight();
34324
34325         if(auto){
34326             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
34327             var newHeight = ch + c.getBorderWidth("tb");
34328             if(g.maxHeight){
34329                 newHeight = Math.min(g.maxHeight, newHeight);
34330             }
34331             c.setHeight(newHeight);
34332         }
34333
34334         if(g.autoWidth){
34335             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
34336         }
34337
34338         var s = this.scroller;
34339
34340         var csize = c.getSize(true);
34341
34342         this.el.setSize(csize.width, csize.height);
34343
34344         this.headerPanel.setWidth(csize.width);
34345         this.footerPanel.setWidth(csize.width);
34346
34347         var hdHeight = this.mainHd.getHeight();
34348         var vw = csize.width;
34349         var vh = csize.height - (tbh + bbh);
34350
34351         s.setSize(vw, vh);
34352
34353         var bt = this.getBodyTable();
34354         
34355         if(cm.getLockedCount() == cm.config.length){
34356             bt = this.getLockedTable();
34357         }
34358         
34359         var ltWidth = hasLock ?
34360                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
34361
34362         var scrollHeight = bt.offsetHeight;
34363         var scrollWidth = ltWidth + bt.offsetWidth;
34364         var vscroll = false, hscroll = false;
34365
34366         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
34367
34368         var lw = this.lockedWrap, mw = this.mainWrap;
34369         var lb = this.lockedBody, mb = this.mainBody;
34370
34371         setTimeout(function(){
34372             var t = s.dom.offsetTop;
34373             var w = s.dom.clientWidth,
34374                 h = s.dom.clientHeight;
34375
34376             lw.setTop(t);
34377             lw.setSize(ltWidth, h);
34378
34379             mw.setLeftTop(ltWidth, t);
34380             mw.setSize(w-ltWidth, h);
34381
34382             lb.setHeight(h-hdHeight);
34383             mb.setHeight(h-hdHeight);
34384
34385             if(is2ndPass !== true && !gv.userResized && expandCol){
34386                 // high speed resize without full column calculation
34387                 
34388                 var ci = cm.getIndexById(expandCol);
34389                 if (ci < 0) {
34390                     ci = cm.findColumnIndex(expandCol);
34391                 }
34392                 ci = Math.max(0, ci); // make sure it's got at least the first col.
34393                 var expandId = cm.getColumnId(ci);
34394                 var  tw = cm.getTotalWidth(false);
34395                 var currentWidth = cm.getColumnWidth(ci);
34396                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
34397                 if(currentWidth != cw){
34398                     cm.setColumnWidth(ci, cw, true);
34399                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34400                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34401                     gv.updateSplitters();
34402                     gv.layout(false, true);
34403                 }
34404             }
34405
34406             if(initialRender){
34407                 lw.show();
34408                 mw.show();
34409             }
34410             //c.endMeasure();
34411         }, 10);
34412     },
34413
34414     onWindowResize : function(){
34415         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
34416             return;
34417         }
34418         this.layout();
34419     },
34420
34421     appendFooter : function(parentEl){
34422         return null;
34423     },
34424
34425     sortAscText : "Sort Ascending",
34426     sortDescText : "Sort Descending",
34427     lockText : "Lock Column",
34428     unlockText : "Unlock Column",
34429     columnsText : "Columns",
34430  
34431     columnsWiderText : "Wider",
34432     columnsNarrowText : "Thinner"
34433 });
34434
34435
34436 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
34437     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
34438     this.proxy.el.addClass('x-grid3-col-dd');
34439 };
34440
34441 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
34442     handleMouseDown : function(e){
34443
34444     },
34445
34446     callHandleMouseDown : function(e){
34447         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
34448     }
34449 });
34450 /*
34451  * Based on:
34452  * Ext JS Library 1.1.1
34453  * Copyright(c) 2006-2007, Ext JS, LLC.
34454  *
34455  * Originally Released Under LGPL - original licence link has changed is not relivant.
34456  *
34457  * Fork - LGPL
34458  * <script type="text/javascript">
34459  */
34460  
34461 // private
34462 // This is a support class used internally by the Grid components
34463 Roo.grid.SplitDragZone = function(grid, hd, hd2){
34464     this.grid = grid;
34465     this.view = grid.getView();
34466     this.proxy = this.view.resizeProxy;
34467     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
34468         "gridSplitters" + this.grid.getGridEl().id, {
34469         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
34470     });
34471     this.setHandleElId(Roo.id(hd));
34472     this.setOuterHandleElId(Roo.id(hd2));
34473     this.scroll = false;
34474 };
34475 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
34476     fly: Roo.Element.fly,
34477
34478     b4StartDrag : function(x, y){
34479         this.view.headersDisabled = true;
34480         this.proxy.setHeight(this.view.mainWrap.getHeight());
34481         var w = this.cm.getColumnWidth(this.cellIndex);
34482         var minw = Math.max(w-this.grid.minColumnWidth, 0);
34483         this.resetConstraints();
34484         this.setXConstraint(minw, 1000);
34485         this.setYConstraint(0, 0);
34486         this.minX = x - minw;
34487         this.maxX = x + 1000;
34488         this.startPos = x;
34489         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
34490     },
34491
34492
34493     handleMouseDown : function(e){
34494         ev = Roo.EventObject.setEvent(e);
34495         var t = this.fly(ev.getTarget());
34496         if(t.hasClass("x-grid-split")){
34497             this.cellIndex = this.view.getCellIndex(t.dom);
34498             this.split = t.dom;
34499             this.cm = this.grid.colModel;
34500             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
34501                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
34502             }
34503         }
34504     },
34505
34506     endDrag : function(e){
34507         this.view.headersDisabled = false;
34508         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
34509         var diff = endX - this.startPos;
34510         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
34511     },
34512
34513     autoOffset : function(){
34514         this.setDelta(0,0);
34515     }
34516 });/*
34517  * Based on:
34518  * Ext JS Library 1.1.1
34519  * Copyright(c) 2006-2007, Ext JS, LLC.
34520  *
34521  * Originally Released Under LGPL - original licence link has changed is not relivant.
34522  *
34523  * Fork - LGPL
34524  * <script type="text/javascript">
34525  */
34526  
34527 // private
34528 // This is a support class used internally by the Grid components
34529 Roo.grid.GridDragZone = function(grid, config){
34530     this.view = grid.getView();
34531     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
34532     if(this.view.lockedBody){
34533         this.setHandleElId(Roo.id(this.view.mainBody.dom));
34534         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
34535     }
34536     this.scroll = false;
34537     this.grid = grid;
34538     this.ddel = document.createElement('div');
34539     this.ddel.className = 'x-grid-dd-wrap';
34540 };
34541
34542 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
34543     ddGroup : "GridDD",
34544
34545     getDragData : function(e){
34546         var t = Roo.lib.Event.getTarget(e);
34547         var rowIndex = this.view.findRowIndex(t);
34548         var sm = this.grid.selModel;
34549             
34550         //Roo.log(rowIndex);
34551         
34552         if (sm.getSelectedCell) {
34553             // cell selection..
34554             if (!sm.getSelectedCell()) {
34555                 return false;
34556             }
34557             if (rowIndex != sm.getSelectedCell()[0]) {
34558                 return false;
34559             }
34560         
34561         }
34562         
34563         if(rowIndex !== false){
34564             
34565             // if editorgrid.. 
34566             
34567             
34568             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
34569                
34570             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
34571               //  
34572             //}
34573             if (e.hasModifier()){
34574                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
34575             }
34576             
34577             Roo.log("getDragData");
34578             
34579             return {
34580                 grid: this.grid,
34581                 ddel: this.ddel,
34582                 rowIndex: rowIndex,
34583                 selections:sm.getSelections ? sm.getSelections() : (
34584                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
34585                 )
34586             };
34587         }
34588         return false;
34589     },
34590
34591     onInitDrag : function(e){
34592         var data = this.dragData;
34593         this.ddel.innerHTML = this.grid.getDragDropText();
34594         this.proxy.update(this.ddel);
34595         // fire start drag?
34596     },
34597
34598     afterRepair : function(){
34599         this.dragging = false;
34600     },
34601
34602     getRepairXY : function(e, data){
34603         return false;
34604     },
34605
34606     onEndDrag : function(data, e){
34607         // fire end drag?
34608     },
34609
34610     onValidDrop : function(dd, e, id){
34611         // fire drag drop?
34612         this.hideProxy();
34613     },
34614
34615     beforeInvalidDrop : function(e, id){
34616
34617     }
34618 });/*
34619  * Based on:
34620  * Ext JS Library 1.1.1
34621  * Copyright(c) 2006-2007, Ext JS, LLC.
34622  *
34623  * Originally Released Under LGPL - original licence link has changed is not relivant.
34624  *
34625  * Fork - LGPL
34626  * <script type="text/javascript">
34627  */
34628  
34629
34630 /**
34631  * @class Roo.grid.ColumnModel
34632  * @extends Roo.util.Observable
34633  * This is the default implementation of a ColumnModel used by the Grid. It defines
34634  * the columns in the grid.
34635  * <br>Usage:<br>
34636  <pre><code>
34637  var colModel = new Roo.grid.ColumnModel([
34638         {header: "Ticker", width: 60, sortable: true, locked: true},
34639         {header: "Company Name", width: 150, sortable: true},
34640         {header: "Market Cap.", width: 100, sortable: true},
34641         {header: "$ Sales", width: 100, sortable: true, renderer: money},
34642         {header: "Employees", width: 100, sortable: true, resizable: false}
34643  ]);
34644  </code></pre>
34645  * <p>
34646  
34647  * The config options listed for this class are options which may appear in each
34648  * individual column definition.
34649  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
34650  * @constructor
34651  * @param {Object} config An Array of column config objects. See this class's
34652  * config objects for details.
34653 */
34654 Roo.grid.ColumnModel = function(config){
34655         /**
34656      * The config passed into the constructor
34657      */
34658     this.config = config;
34659     this.lookup = {};
34660
34661     // if no id, create one
34662     // if the column does not have a dataIndex mapping,
34663     // map it to the order it is in the config
34664     for(var i = 0, len = config.length; i < len; i++){
34665         var c = config[i];
34666         if(typeof c.dataIndex == "undefined"){
34667             c.dataIndex = i;
34668         }
34669         if(typeof c.renderer == "string"){
34670             c.renderer = Roo.util.Format[c.renderer];
34671         }
34672         if(typeof c.id == "undefined"){
34673             c.id = Roo.id();
34674         }
34675         if(c.editor && c.editor.xtype){
34676             c.editor  = Roo.factory(c.editor, Roo.grid);
34677         }
34678         if(c.editor && c.editor.isFormField){
34679             c.editor = new Roo.grid.GridEditor(c.editor);
34680         }
34681         this.lookup[c.id] = c;
34682     }
34683
34684     /**
34685      * The width of columns which have no width specified (defaults to 100)
34686      * @type Number
34687      */
34688     this.defaultWidth = 100;
34689
34690     /**
34691      * Default sortable of columns which have no sortable specified (defaults to false)
34692      * @type Boolean
34693      */
34694     this.defaultSortable = false;
34695
34696     this.addEvents({
34697         /**
34698              * @event widthchange
34699              * Fires when the width of a column changes.
34700              * @param {ColumnModel} this
34701              * @param {Number} columnIndex The column index
34702              * @param {Number} newWidth The new width
34703              */
34704             "widthchange": true,
34705         /**
34706              * @event headerchange
34707              * Fires when the text of a header changes.
34708              * @param {ColumnModel} this
34709              * @param {Number} columnIndex The column index
34710              * @param {Number} newText The new header text
34711              */
34712             "headerchange": true,
34713         /**
34714              * @event hiddenchange
34715              * Fires when a column is hidden or "unhidden".
34716              * @param {ColumnModel} this
34717              * @param {Number} columnIndex The column index
34718              * @param {Boolean} hidden true if hidden, false otherwise
34719              */
34720             "hiddenchange": true,
34721             /**
34722          * @event columnmoved
34723          * Fires when a column is moved.
34724          * @param {ColumnModel} this
34725          * @param {Number} oldIndex
34726          * @param {Number} newIndex
34727          */
34728         "columnmoved" : true,
34729         /**
34730          * @event columlockchange
34731          * Fires when a column's locked state is changed
34732          * @param {ColumnModel} this
34733          * @param {Number} colIndex
34734          * @param {Boolean} locked true if locked
34735          */
34736         "columnlockchange" : true
34737     });
34738     Roo.grid.ColumnModel.superclass.constructor.call(this);
34739 };
34740 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
34741     /**
34742      * @cfg {String} header The header text to display in the Grid view.
34743      */
34744     /**
34745      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
34746      * {@link Roo.data.Record} definition from which to draw the column's value. If not
34747      * specified, the column's index is used as an index into the Record's data Array.
34748      */
34749     /**
34750      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
34751      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
34752      */
34753     /**
34754      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
34755      * Defaults to the value of the {@link #defaultSortable} property.
34756      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
34757      */
34758     /**
34759      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
34760      */
34761     /**
34762      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
34763      */
34764     /**
34765      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
34766      */
34767     /**
34768      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
34769      */
34770     /**
34771      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
34772      * given the cell's data value. See {@link #setRenderer}. If not specified, the
34773      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
34774      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
34775      */
34776        /**
34777      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
34778      */
34779     /**
34780      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
34781      */
34782     /**
34783      * @cfg {String} cursor (Optional)
34784      */
34785     /**
34786      * @cfg {String} tooltip (Optional)
34787      */
34788     /**
34789      * @cfg {Number} xs (Optional)
34790      */
34791     /**
34792      * @cfg {Number} sm (Optional)
34793      */
34794     /**
34795      * @cfg {Number} md (Optional)
34796      */
34797     /**
34798      * @cfg {Number} lg (Optional)
34799      */
34800     /**
34801      * Returns the id of the column at the specified index.
34802      * @param {Number} index The column index
34803      * @return {String} the id
34804      */
34805     getColumnId : function(index){
34806         return this.config[index].id;
34807     },
34808
34809     /**
34810      * Returns the column for a specified id.
34811      * @param {String} id The column id
34812      * @return {Object} the column
34813      */
34814     getColumnById : function(id){
34815         return this.lookup[id];
34816     },
34817
34818     
34819     /**
34820      * Returns the column for a specified dataIndex.
34821      * @param {String} dataIndex The column dataIndex
34822      * @return {Object|Boolean} the column or false if not found
34823      */
34824     getColumnByDataIndex: function(dataIndex){
34825         var index = this.findColumnIndex(dataIndex);
34826         return index > -1 ? this.config[index] : false;
34827     },
34828     
34829     /**
34830      * Returns the index for a specified column id.
34831      * @param {String} id The column id
34832      * @return {Number} the index, or -1 if not found
34833      */
34834     getIndexById : function(id){
34835         for(var i = 0, len = this.config.length; i < len; i++){
34836             if(this.config[i].id == id){
34837                 return i;
34838             }
34839         }
34840         return -1;
34841     },
34842     
34843     /**
34844      * Returns the index for a specified column dataIndex.
34845      * @param {String} dataIndex The column dataIndex
34846      * @return {Number} the index, or -1 if not found
34847      */
34848     
34849     findColumnIndex : function(dataIndex){
34850         for(var i = 0, len = this.config.length; i < len; i++){
34851             if(this.config[i].dataIndex == dataIndex){
34852                 return i;
34853             }
34854         }
34855         return -1;
34856     },
34857     
34858     
34859     moveColumn : function(oldIndex, newIndex){
34860         var c = this.config[oldIndex];
34861         this.config.splice(oldIndex, 1);
34862         this.config.splice(newIndex, 0, c);
34863         this.dataMap = null;
34864         this.fireEvent("columnmoved", this, oldIndex, newIndex);
34865     },
34866
34867     isLocked : function(colIndex){
34868         return this.config[colIndex].locked === true;
34869     },
34870
34871     setLocked : function(colIndex, value, suppressEvent){
34872         if(this.isLocked(colIndex) == value){
34873             return;
34874         }
34875         this.config[colIndex].locked = value;
34876         if(!suppressEvent){
34877             this.fireEvent("columnlockchange", this, colIndex, value);
34878         }
34879     },
34880
34881     getTotalLockedWidth : function(){
34882         var totalWidth = 0;
34883         for(var i = 0; i < this.config.length; i++){
34884             if(this.isLocked(i) && !this.isHidden(i)){
34885                 this.totalWidth += this.getColumnWidth(i);
34886             }
34887         }
34888         return totalWidth;
34889     },
34890
34891     getLockedCount : function(){
34892         for(var i = 0, len = this.config.length; i < len; i++){
34893             if(!this.isLocked(i)){
34894                 return i;
34895             }
34896         }
34897         
34898         return this.config.length;
34899     },
34900
34901     /**
34902      * Returns the number of columns.
34903      * @return {Number}
34904      */
34905     getColumnCount : function(visibleOnly){
34906         if(visibleOnly === true){
34907             var c = 0;
34908             for(var i = 0, len = this.config.length; i < len; i++){
34909                 if(!this.isHidden(i)){
34910                     c++;
34911                 }
34912             }
34913             return c;
34914         }
34915         return this.config.length;
34916     },
34917
34918     /**
34919      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
34920      * @param {Function} fn
34921      * @param {Object} scope (optional)
34922      * @return {Array} result
34923      */
34924     getColumnsBy : function(fn, scope){
34925         var r = [];
34926         for(var i = 0, len = this.config.length; i < len; i++){
34927             var c = this.config[i];
34928             if(fn.call(scope||this, c, i) === true){
34929                 r[r.length] = c;
34930             }
34931         }
34932         return r;
34933     },
34934
34935     /**
34936      * Returns true if the specified column is sortable.
34937      * @param {Number} col The column index
34938      * @return {Boolean}
34939      */
34940     isSortable : function(col){
34941         if(typeof this.config[col].sortable == "undefined"){
34942             return this.defaultSortable;
34943         }
34944         return this.config[col].sortable;
34945     },
34946
34947     /**
34948      * Returns the rendering (formatting) function defined for the column.
34949      * @param {Number} col The column index.
34950      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
34951      */
34952     getRenderer : function(col){
34953         if(!this.config[col].renderer){
34954             return Roo.grid.ColumnModel.defaultRenderer;
34955         }
34956         return this.config[col].renderer;
34957     },
34958
34959     /**
34960      * Sets the rendering (formatting) function for a column.
34961      * @param {Number} col The column index
34962      * @param {Function} fn The function to use to process the cell's raw data
34963      * to return HTML markup for the grid view. The render function is called with
34964      * the following parameters:<ul>
34965      * <li>Data value.</li>
34966      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
34967      * <li>css A CSS style string to apply to the table cell.</li>
34968      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
34969      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
34970      * <li>Row index</li>
34971      * <li>Column index</li>
34972      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
34973      */
34974     setRenderer : function(col, fn){
34975         this.config[col].renderer = fn;
34976     },
34977
34978     /**
34979      * Returns the width for the specified column.
34980      * @param {Number} col The column index
34981      * @return {Number}
34982      */
34983     getColumnWidth : function(col){
34984         return this.config[col].width * 1 || this.defaultWidth;
34985     },
34986
34987     /**
34988      * Sets the width for a column.
34989      * @param {Number} col The column index
34990      * @param {Number} width The new width
34991      */
34992     setColumnWidth : function(col, width, suppressEvent){
34993         this.config[col].width = width;
34994         this.totalWidth = null;
34995         if(!suppressEvent){
34996              this.fireEvent("widthchange", this, col, width);
34997         }
34998     },
34999
35000     /**
35001      * Returns the total width of all columns.
35002      * @param {Boolean} includeHidden True to include hidden column widths
35003      * @return {Number}
35004      */
35005     getTotalWidth : function(includeHidden){
35006         if(!this.totalWidth){
35007             this.totalWidth = 0;
35008             for(var i = 0, len = this.config.length; i < len; i++){
35009                 if(includeHidden || !this.isHidden(i)){
35010                     this.totalWidth += this.getColumnWidth(i);
35011                 }
35012             }
35013         }
35014         return this.totalWidth;
35015     },
35016
35017     /**
35018      * Returns the header for the specified column.
35019      * @param {Number} col The column index
35020      * @return {String}
35021      */
35022     getColumnHeader : function(col){
35023         return this.config[col].header;
35024     },
35025
35026     /**
35027      * Sets the header for a column.
35028      * @param {Number} col The column index
35029      * @param {String} header The new header
35030      */
35031     setColumnHeader : function(col, header){
35032         this.config[col].header = header;
35033         this.fireEvent("headerchange", this, col, header);
35034     },
35035
35036     /**
35037      * Returns the tooltip for the specified column.
35038      * @param {Number} col The column index
35039      * @return {String}
35040      */
35041     getColumnTooltip : function(col){
35042             return this.config[col].tooltip;
35043     },
35044     /**
35045      * Sets the tooltip for a column.
35046      * @param {Number} col The column index
35047      * @param {String} tooltip The new tooltip
35048      */
35049     setColumnTooltip : function(col, tooltip){
35050             this.config[col].tooltip = tooltip;
35051     },
35052
35053     /**
35054      * Returns the dataIndex for the specified column.
35055      * @param {Number} col The column index
35056      * @return {Number}
35057      */
35058     getDataIndex : function(col){
35059         return this.config[col].dataIndex;
35060     },
35061
35062     /**
35063      * Sets the dataIndex for a column.
35064      * @param {Number} col The column index
35065      * @param {Number} dataIndex The new dataIndex
35066      */
35067     setDataIndex : function(col, dataIndex){
35068         this.config[col].dataIndex = dataIndex;
35069     },
35070
35071     
35072     
35073     /**
35074      * Returns true if the cell is editable.
35075      * @param {Number} colIndex The column index
35076      * @param {Number} rowIndex The row index - this is nto actually used..?
35077      * @return {Boolean}
35078      */
35079     isCellEditable : function(colIndex, rowIndex){
35080         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
35081     },
35082
35083     /**
35084      * Returns the editor defined for the cell/column.
35085      * return false or null to disable editing.
35086      * @param {Number} colIndex The column index
35087      * @param {Number} rowIndex The row index
35088      * @return {Object}
35089      */
35090     getCellEditor : function(colIndex, rowIndex){
35091         return this.config[colIndex].editor;
35092     },
35093
35094     /**
35095      * Sets if a column is editable.
35096      * @param {Number} col The column index
35097      * @param {Boolean} editable True if the column is editable
35098      */
35099     setEditable : function(col, editable){
35100         this.config[col].editable = editable;
35101     },
35102
35103
35104     /**
35105      * Returns true if the column is hidden.
35106      * @param {Number} colIndex The column index
35107      * @return {Boolean}
35108      */
35109     isHidden : function(colIndex){
35110         return this.config[colIndex].hidden;
35111     },
35112
35113
35114     /**
35115      * Returns true if the column width cannot be changed
35116      */
35117     isFixed : function(colIndex){
35118         return this.config[colIndex].fixed;
35119     },
35120
35121     /**
35122      * Returns true if the column can be resized
35123      * @return {Boolean}
35124      */
35125     isResizable : function(colIndex){
35126         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
35127     },
35128     /**
35129      * Sets if a column is hidden.
35130      * @param {Number} colIndex The column index
35131      * @param {Boolean} hidden True if the column is hidden
35132      */
35133     setHidden : function(colIndex, hidden){
35134         this.config[colIndex].hidden = hidden;
35135         this.totalWidth = null;
35136         this.fireEvent("hiddenchange", this, colIndex, hidden);
35137     },
35138
35139     /**
35140      * Sets the editor for a column.
35141      * @param {Number} col The column index
35142      * @param {Object} editor The editor object
35143      */
35144     setEditor : function(col, editor){
35145         this.config[col].editor = editor;
35146     }
35147 });
35148
35149 Roo.grid.ColumnModel.defaultRenderer = function(value)
35150 {
35151     if(typeof value == "object") {
35152         return value;
35153     }
35154         if(typeof value == "string" && value.length < 1){
35155             return "&#160;";
35156         }
35157     
35158         return String.format("{0}", value);
35159 };
35160
35161 // Alias for backwards compatibility
35162 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
35163 /*
35164  * Based on:
35165  * Ext JS Library 1.1.1
35166  * Copyright(c) 2006-2007, Ext JS, LLC.
35167  *
35168  * Originally Released Under LGPL - original licence link has changed is not relivant.
35169  *
35170  * Fork - LGPL
35171  * <script type="text/javascript">
35172  */
35173
35174 /**
35175  * @class Roo.grid.AbstractSelectionModel
35176  * @extends Roo.util.Observable
35177  * Abstract base class for grid SelectionModels.  It provides the interface that should be
35178  * implemented by descendant classes.  This class should not be directly instantiated.
35179  * @constructor
35180  */
35181 Roo.grid.AbstractSelectionModel = function(){
35182     this.locked = false;
35183     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
35184 };
35185
35186 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
35187     /** @ignore Called by the grid automatically. Do not call directly. */
35188     init : function(grid){
35189         this.grid = grid;
35190         this.initEvents();
35191     },
35192
35193     /**
35194      * Locks the selections.
35195      */
35196     lock : function(){
35197         this.locked = true;
35198     },
35199
35200     /**
35201      * Unlocks the selections.
35202      */
35203     unlock : function(){
35204         this.locked = false;
35205     },
35206
35207     /**
35208      * Returns true if the selections are locked.
35209      * @return {Boolean}
35210      */
35211     isLocked : function(){
35212         return this.locked;
35213     }
35214 });/*
35215  * Based on:
35216  * Ext JS Library 1.1.1
35217  * Copyright(c) 2006-2007, Ext JS, LLC.
35218  *
35219  * Originally Released Under LGPL - original licence link has changed is not relivant.
35220  *
35221  * Fork - LGPL
35222  * <script type="text/javascript">
35223  */
35224 /**
35225  * @extends Roo.grid.AbstractSelectionModel
35226  * @class Roo.grid.RowSelectionModel
35227  * The default SelectionModel used by {@link Roo.grid.Grid}.
35228  * It supports multiple selections and keyboard selection/navigation. 
35229  * @constructor
35230  * @param {Object} config
35231  */
35232 Roo.grid.RowSelectionModel = function(config){
35233     Roo.apply(this, config);
35234     this.selections = new Roo.util.MixedCollection(false, function(o){
35235         return o.id;
35236     });
35237
35238     this.last = false;
35239     this.lastActive = false;
35240
35241     this.addEvents({
35242         /**
35243              * @event selectionchange
35244              * Fires when the selection changes
35245              * @param {SelectionModel} this
35246              */
35247             "selectionchange" : true,
35248         /**
35249              * @event afterselectionchange
35250              * Fires after the selection changes (eg. by key press or clicking)
35251              * @param {SelectionModel} this
35252              */
35253             "afterselectionchange" : true,
35254         /**
35255              * @event beforerowselect
35256              * Fires when a row is selected being selected, return false to cancel.
35257              * @param {SelectionModel} this
35258              * @param {Number} rowIndex The selected index
35259              * @param {Boolean} keepExisting False if other selections will be cleared
35260              */
35261             "beforerowselect" : true,
35262         /**
35263              * @event rowselect
35264              * Fires when a row is selected.
35265              * @param {SelectionModel} this
35266              * @param {Number} rowIndex The selected index
35267              * @param {Roo.data.Record} r The record
35268              */
35269             "rowselect" : true,
35270         /**
35271              * @event rowdeselect
35272              * Fires when a row is deselected.
35273              * @param {SelectionModel} this
35274              * @param {Number} rowIndex The selected index
35275              */
35276         "rowdeselect" : true
35277     });
35278     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
35279     this.locked = false;
35280 };
35281
35282 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
35283     /**
35284      * @cfg {Boolean} singleSelect
35285      * True to allow selection of only one row at a time (defaults to false)
35286      */
35287     singleSelect : false,
35288
35289     // private
35290     initEvents : function(){
35291
35292         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
35293             this.grid.on("mousedown", this.handleMouseDown, this);
35294         }else{ // allow click to work like normal
35295             this.grid.on("rowclick", this.handleDragableRowClick, this);
35296         }
35297
35298         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
35299             "up" : function(e){
35300                 if(!e.shiftKey){
35301                     this.selectPrevious(e.shiftKey);
35302                 }else if(this.last !== false && this.lastActive !== false){
35303                     var last = this.last;
35304                     this.selectRange(this.last,  this.lastActive-1);
35305                     this.grid.getView().focusRow(this.lastActive);
35306                     if(last !== false){
35307                         this.last = last;
35308                     }
35309                 }else{
35310                     this.selectFirstRow();
35311                 }
35312                 this.fireEvent("afterselectionchange", this);
35313             },
35314             "down" : function(e){
35315                 if(!e.shiftKey){
35316                     this.selectNext(e.shiftKey);
35317                 }else if(this.last !== false && this.lastActive !== false){
35318                     var last = this.last;
35319                     this.selectRange(this.last,  this.lastActive+1);
35320                     this.grid.getView().focusRow(this.lastActive);
35321                     if(last !== false){
35322                         this.last = last;
35323                     }
35324                 }else{
35325                     this.selectFirstRow();
35326                 }
35327                 this.fireEvent("afterselectionchange", this);
35328             },
35329             scope: this
35330         });
35331
35332         var view = this.grid.view;
35333         view.on("refresh", this.onRefresh, this);
35334         view.on("rowupdated", this.onRowUpdated, this);
35335         view.on("rowremoved", this.onRemove, this);
35336     },
35337
35338     // private
35339     onRefresh : function(){
35340         var ds = this.grid.dataSource, i, v = this.grid.view;
35341         var s = this.selections;
35342         s.each(function(r){
35343             if((i = ds.indexOfId(r.id)) != -1){
35344                 v.onRowSelect(i);
35345                 s.add(ds.getAt(i)); // updating the selection relate data
35346             }else{
35347                 s.remove(r);
35348             }
35349         });
35350     },
35351
35352     // private
35353     onRemove : function(v, index, r){
35354         this.selections.remove(r);
35355     },
35356
35357     // private
35358     onRowUpdated : function(v, index, r){
35359         if(this.isSelected(r)){
35360             v.onRowSelect(index);
35361         }
35362     },
35363
35364     /**
35365      * Select records.
35366      * @param {Array} records The records to select
35367      * @param {Boolean} keepExisting (optional) True to keep existing selections
35368      */
35369     selectRecords : function(records, keepExisting){
35370         if(!keepExisting){
35371             this.clearSelections();
35372         }
35373         var ds = this.grid.dataSource;
35374         for(var i = 0, len = records.length; i < len; i++){
35375             this.selectRow(ds.indexOf(records[i]), true);
35376         }
35377     },
35378
35379     /**
35380      * Gets the number of selected rows.
35381      * @return {Number}
35382      */
35383     getCount : function(){
35384         return this.selections.length;
35385     },
35386
35387     /**
35388      * Selects the first row in the grid.
35389      */
35390     selectFirstRow : function(){
35391         this.selectRow(0);
35392     },
35393
35394     /**
35395      * Select the last row.
35396      * @param {Boolean} keepExisting (optional) True to keep existing selections
35397      */
35398     selectLastRow : function(keepExisting){
35399         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
35400     },
35401
35402     /**
35403      * Selects the row immediately following the last selected row.
35404      * @param {Boolean} keepExisting (optional) True to keep existing selections
35405      */
35406     selectNext : function(keepExisting){
35407         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
35408             this.selectRow(this.last+1, keepExisting);
35409             this.grid.getView().focusRow(this.last);
35410         }
35411     },
35412
35413     /**
35414      * Selects the row that precedes the last selected row.
35415      * @param {Boolean} keepExisting (optional) True to keep existing selections
35416      */
35417     selectPrevious : function(keepExisting){
35418         if(this.last){
35419             this.selectRow(this.last-1, keepExisting);
35420             this.grid.getView().focusRow(this.last);
35421         }
35422     },
35423
35424     /**
35425      * Returns the selected records
35426      * @return {Array} Array of selected records
35427      */
35428     getSelections : function(){
35429         return [].concat(this.selections.items);
35430     },
35431
35432     /**
35433      * Returns the first selected record.
35434      * @return {Record}
35435      */
35436     getSelected : function(){
35437         return this.selections.itemAt(0);
35438     },
35439
35440
35441     /**
35442      * Clears all selections.
35443      */
35444     clearSelections : function(fast){
35445         if(this.locked) {
35446             return;
35447         }
35448         if(fast !== true){
35449             var ds = this.grid.dataSource;
35450             var s = this.selections;
35451             s.each(function(r){
35452                 this.deselectRow(ds.indexOfId(r.id));
35453             }, this);
35454             s.clear();
35455         }else{
35456             this.selections.clear();
35457         }
35458         this.last = false;
35459     },
35460
35461
35462     /**
35463      * Selects all rows.
35464      */
35465     selectAll : function(){
35466         if(this.locked) {
35467             return;
35468         }
35469         this.selections.clear();
35470         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
35471             this.selectRow(i, true);
35472         }
35473     },
35474
35475     /**
35476      * Returns True if there is a selection.
35477      * @return {Boolean}
35478      */
35479     hasSelection : function(){
35480         return this.selections.length > 0;
35481     },
35482
35483     /**
35484      * Returns True if the specified row is selected.
35485      * @param {Number/Record} record The record or index of the record to check
35486      * @return {Boolean}
35487      */
35488     isSelected : function(index){
35489         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
35490         return (r && this.selections.key(r.id) ? true : false);
35491     },
35492
35493     /**
35494      * Returns True if the specified record id is selected.
35495      * @param {String} id The id of record to check
35496      * @return {Boolean}
35497      */
35498     isIdSelected : function(id){
35499         return (this.selections.key(id) ? true : false);
35500     },
35501
35502     // private
35503     handleMouseDown : function(e, t){
35504         var view = this.grid.getView(), rowIndex;
35505         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
35506             return;
35507         };
35508         if(e.shiftKey && this.last !== false){
35509             var last = this.last;
35510             this.selectRange(last, rowIndex, e.ctrlKey);
35511             this.last = last; // reset the last
35512             view.focusRow(rowIndex);
35513         }else{
35514             var isSelected = this.isSelected(rowIndex);
35515             if(e.button !== 0 && isSelected){
35516                 view.focusRow(rowIndex);
35517             }else if(e.ctrlKey && isSelected){
35518                 this.deselectRow(rowIndex);
35519             }else if(!isSelected){
35520                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
35521                 view.focusRow(rowIndex);
35522             }
35523         }
35524         this.fireEvent("afterselectionchange", this);
35525     },
35526     // private
35527     handleDragableRowClick :  function(grid, rowIndex, e) 
35528     {
35529         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
35530             this.selectRow(rowIndex, false);
35531             grid.view.focusRow(rowIndex);
35532              this.fireEvent("afterselectionchange", this);
35533         }
35534     },
35535     
35536     /**
35537      * Selects multiple rows.
35538      * @param {Array} rows Array of the indexes of the row to select
35539      * @param {Boolean} keepExisting (optional) True to keep existing selections
35540      */
35541     selectRows : function(rows, keepExisting){
35542         if(!keepExisting){
35543             this.clearSelections();
35544         }
35545         for(var i = 0, len = rows.length; i < len; i++){
35546             this.selectRow(rows[i], true);
35547         }
35548     },
35549
35550     /**
35551      * Selects a range of rows. All rows in between startRow and endRow are also selected.
35552      * @param {Number} startRow The index of the first row in the range
35553      * @param {Number} endRow The index of the last row in the range
35554      * @param {Boolean} keepExisting (optional) True to retain existing selections
35555      */
35556     selectRange : function(startRow, endRow, keepExisting){
35557         if(this.locked) {
35558             return;
35559         }
35560         if(!keepExisting){
35561             this.clearSelections();
35562         }
35563         if(startRow <= endRow){
35564             for(var i = startRow; i <= endRow; i++){
35565                 this.selectRow(i, true);
35566             }
35567         }else{
35568             for(var i = startRow; i >= endRow; i--){
35569                 this.selectRow(i, true);
35570             }
35571         }
35572     },
35573
35574     /**
35575      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
35576      * @param {Number} startRow The index of the first row in the range
35577      * @param {Number} endRow The index of the last row in the range
35578      */
35579     deselectRange : function(startRow, endRow, preventViewNotify){
35580         if(this.locked) {
35581             return;
35582         }
35583         for(var i = startRow; i <= endRow; i++){
35584             this.deselectRow(i, preventViewNotify);
35585         }
35586     },
35587
35588     /**
35589      * Selects a row.
35590      * @param {Number} row The index of the row to select
35591      * @param {Boolean} keepExisting (optional) True to keep existing selections
35592      */
35593     selectRow : function(index, keepExisting, preventViewNotify){
35594         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
35595             return;
35596         }
35597         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
35598             if(!keepExisting || this.singleSelect){
35599                 this.clearSelections();
35600             }
35601             var r = this.grid.dataSource.getAt(index);
35602             this.selections.add(r);
35603             this.last = this.lastActive = index;
35604             if(!preventViewNotify){
35605                 this.grid.getView().onRowSelect(index);
35606             }
35607             this.fireEvent("rowselect", this, index, r);
35608             this.fireEvent("selectionchange", this);
35609         }
35610     },
35611
35612     /**
35613      * Deselects a row.
35614      * @param {Number} row The index of the row to deselect
35615      */
35616     deselectRow : function(index, preventViewNotify){
35617         if(this.locked) {
35618             return;
35619         }
35620         if(this.last == index){
35621             this.last = false;
35622         }
35623         if(this.lastActive == index){
35624             this.lastActive = false;
35625         }
35626         var r = this.grid.dataSource.getAt(index);
35627         this.selections.remove(r);
35628         if(!preventViewNotify){
35629             this.grid.getView().onRowDeselect(index);
35630         }
35631         this.fireEvent("rowdeselect", this, index);
35632         this.fireEvent("selectionchange", this);
35633     },
35634
35635     // private
35636     restoreLast : function(){
35637         if(this._last){
35638             this.last = this._last;
35639         }
35640     },
35641
35642     // private
35643     acceptsNav : function(row, col, cm){
35644         return !cm.isHidden(col) && cm.isCellEditable(col, row);
35645     },
35646
35647     // private
35648     onEditorKey : function(field, e){
35649         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
35650         if(k == e.TAB){
35651             e.stopEvent();
35652             ed.completeEdit();
35653             if(e.shiftKey){
35654                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
35655             }else{
35656                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
35657             }
35658         }else if(k == e.ENTER && !e.ctrlKey){
35659             e.stopEvent();
35660             ed.completeEdit();
35661             if(e.shiftKey){
35662                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
35663             }else{
35664                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
35665             }
35666         }else if(k == e.ESC){
35667             ed.cancelEdit();
35668         }
35669         if(newCell){
35670             g.startEditing(newCell[0], newCell[1]);
35671         }
35672     }
35673 });/*
35674  * Based on:
35675  * Ext JS Library 1.1.1
35676  * Copyright(c) 2006-2007, Ext JS, LLC.
35677  *
35678  * Originally Released Under LGPL - original licence link has changed is not relivant.
35679  *
35680  * Fork - LGPL
35681  * <script type="text/javascript">
35682  */
35683 /**
35684  * @class Roo.grid.CellSelectionModel
35685  * @extends Roo.grid.AbstractSelectionModel
35686  * This class provides the basic implementation for cell selection in a grid.
35687  * @constructor
35688  * @param {Object} config The object containing the configuration of this model.
35689  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
35690  */
35691 Roo.grid.CellSelectionModel = function(config){
35692     Roo.apply(this, config);
35693
35694     this.selection = null;
35695
35696     this.addEvents({
35697         /**
35698              * @event beforerowselect
35699              * Fires before a cell is selected.
35700              * @param {SelectionModel} this
35701              * @param {Number} rowIndex The selected row index
35702              * @param {Number} colIndex The selected cell index
35703              */
35704             "beforecellselect" : true,
35705         /**
35706              * @event cellselect
35707              * Fires when a cell is selected.
35708              * @param {SelectionModel} this
35709              * @param {Number} rowIndex The selected row index
35710              * @param {Number} colIndex The selected cell index
35711              */
35712             "cellselect" : true,
35713         /**
35714              * @event selectionchange
35715              * Fires when the active selection changes.
35716              * @param {SelectionModel} this
35717              * @param {Object} selection null for no selection or an object (o) with two properties
35718                 <ul>
35719                 <li>o.record: the record object for the row the selection is in</li>
35720                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
35721                 </ul>
35722              */
35723             "selectionchange" : true,
35724         /**
35725              * @event tabend
35726              * Fires when the tab (or enter) was pressed on the last editable cell
35727              * You can use this to trigger add new row.
35728              * @param {SelectionModel} this
35729              */
35730             "tabend" : true,
35731          /**
35732              * @event beforeeditnext
35733              * Fires before the next editable sell is made active
35734              * You can use this to skip to another cell or fire the tabend
35735              *    if you set cell to false
35736              * @param {Object} eventdata object : { cell : [ row, col ] } 
35737              */
35738             "beforeeditnext" : true
35739     });
35740     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
35741 };
35742
35743 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
35744     
35745     enter_is_tab: false,
35746
35747     /** @ignore */
35748     initEvents : function(){
35749         this.grid.on("mousedown", this.handleMouseDown, this);
35750         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
35751         var view = this.grid.view;
35752         view.on("refresh", this.onViewChange, this);
35753         view.on("rowupdated", this.onRowUpdated, this);
35754         view.on("beforerowremoved", this.clearSelections, this);
35755         view.on("beforerowsinserted", this.clearSelections, this);
35756         if(this.grid.isEditor){
35757             this.grid.on("beforeedit", this.beforeEdit,  this);
35758         }
35759     },
35760
35761         //private
35762     beforeEdit : function(e){
35763         this.select(e.row, e.column, false, true, e.record);
35764     },
35765
35766         //private
35767     onRowUpdated : function(v, index, r){
35768         if(this.selection && this.selection.record == r){
35769             v.onCellSelect(index, this.selection.cell[1]);
35770         }
35771     },
35772
35773         //private
35774     onViewChange : function(){
35775         this.clearSelections(true);
35776     },
35777
35778         /**
35779          * Returns the currently selected cell,.
35780          * @return {Array} The selected cell (row, column) or null if none selected.
35781          */
35782     getSelectedCell : function(){
35783         return this.selection ? this.selection.cell : null;
35784     },
35785
35786     /**
35787      * Clears all selections.
35788      * @param {Boolean} true to prevent the gridview from being notified about the change.
35789      */
35790     clearSelections : function(preventNotify){
35791         var s = this.selection;
35792         if(s){
35793             if(preventNotify !== true){
35794                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
35795             }
35796             this.selection = null;
35797             this.fireEvent("selectionchange", this, null);
35798         }
35799     },
35800
35801     /**
35802      * Returns true if there is a selection.
35803      * @return {Boolean}
35804      */
35805     hasSelection : function(){
35806         return this.selection ? true : false;
35807     },
35808
35809     /** @ignore */
35810     handleMouseDown : function(e, t){
35811         var v = this.grid.getView();
35812         if(this.isLocked()){
35813             return;
35814         };
35815         var row = v.findRowIndex(t);
35816         var cell = v.findCellIndex(t);
35817         if(row !== false && cell !== false){
35818             this.select(row, cell);
35819         }
35820     },
35821
35822     /**
35823      * Selects a cell.
35824      * @param {Number} rowIndex
35825      * @param {Number} collIndex
35826      */
35827     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
35828         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
35829             this.clearSelections();
35830             r = r || this.grid.dataSource.getAt(rowIndex);
35831             this.selection = {
35832                 record : r,
35833                 cell : [rowIndex, colIndex]
35834             };
35835             if(!preventViewNotify){
35836                 var v = this.grid.getView();
35837                 v.onCellSelect(rowIndex, colIndex);
35838                 if(preventFocus !== true){
35839                     v.focusCell(rowIndex, colIndex);
35840                 }
35841             }
35842             this.fireEvent("cellselect", this, rowIndex, colIndex);
35843             this.fireEvent("selectionchange", this, this.selection);
35844         }
35845     },
35846
35847         //private
35848     isSelectable : function(rowIndex, colIndex, cm){
35849         return !cm.isHidden(colIndex);
35850     },
35851
35852     /** @ignore */
35853     handleKeyDown : function(e){
35854         //Roo.log('Cell Sel Model handleKeyDown');
35855         if(!e.isNavKeyPress()){
35856             return;
35857         }
35858         var g = this.grid, s = this.selection;
35859         if(!s){
35860             e.stopEvent();
35861             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
35862             if(cell){
35863                 this.select(cell[0], cell[1]);
35864             }
35865             return;
35866         }
35867         var sm = this;
35868         var walk = function(row, col, step){
35869             return g.walkCells(row, col, step, sm.isSelectable,  sm);
35870         };
35871         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
35872         var newCell;
35873
35874       
35875
35876         switch(k){
35877             case e.TAB:
35878                 // handled by onEditorKey
35879                 if (g.isEditor && g.editing) {
35880                     return;
35881                 }
35882                 if(e.shiftKey) {
35883                     newCell = walk(r, c-1, -1);
35884                 } else {
35885                     newCell = walk(r, c+1, 1);
35886                 }
35887                 break;
35888             
35889             case e.DOWN:
35890                newCell = walk(r+1, c, 1);
35891                 break;
35892             
35893             case e.UP:
35894                 newCell = walk(r-1, c, -1);
35895                 break;
35896             
35897             case e.RIGHT:
35898                 newCell = walk(r, c+1, 1);
35899                 break;
35900             
35901             case e.LEFT:
35902                 newCell = walk(r, c-1, -1);
35903                 break;
35904             
35905             case e.ENTER:
35906                 
35907                 if(g.isEditor && !g.editing){
35908                    g.startEditing(r, c);
35909                    e.stopEvent();
35910                    return;
35911                 }
35912                 
35913                 
35914              break;
35915         };
35916         if(newCell){
35917             this.select(newCell[0], newCell[1]);
35918             e.stopEvent();
35919             
35920         }
35921     },
35922
35923     acceptsNav : function(row, col, cm){
35924         return !cm.isHidden(col) && cm.isCellEditable(col, row);
35925     },
35926     /**
35927      * Selects a cell.
35928      * @param {Number} field (not used) - as it's normally used as a listener
35929      * @param {Number} e - event - fake it by using
35930      *
35931      * var e = Roo.EventObjectImpl.prototype;
35932      * e.keyCode = e.TAB
35933      *
35934      * 
35935      */
35936     onEditorKey : function(field, e){
35937         
35938         var k = e.getKey(),
35939             newCell,
35940             g = this.grid,
35941             ed = g.activeEditor,
35942             forward = false;
35943         ///Roo.log('onEditorKey' + k);
35944         
35945         
35946         if (this.enter_is_tab && k == e.ENTER) {
35947             k = e.TAB;
35948         }
35949         
35950         if(k == e.TAB){
35951             if(e.shiftKey){
35952                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
35953             }else{
35954                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
35955                 forward = true;
35956             }
35957             
35958             e.stopEvent();
35959             
35960         } else if(k == e.ENTER &&  !e.ctrlKey){
35961             ed.completeEdit();
35962             e.stopEvent();
35963             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
35964         
35965                 } else if(k == e.ESC){
35966             ed.cancelEdit();
35967         }
35968                 
35969         if (newCell) {
35970             var ecall = { cell : newCell, forward : forward };
35971             this.fireEvent('beforeeditnext', ecall );
35972             newCell = ecall.cell;
35973                         forward = ecall.forward;
35974         }
35975                 
35976         if(newCell){
35977             //Roo.log('next cell after edit');
35978             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
35979         } else if (forward) {
35980             // tabbed past last
35981             this.fireEvent.defer(100, this, ['tabend',this]);
35982         }
35983     }
35984 });/*
35985  * Based on:
35986  * Ext JS Library 1.1.1
35987  * Copyright(c) 2006-2007, Ext JS, LLC.
35988  *
35989  * Originally Released Under LGPL - original licence link has changed is not relivant.
35990  *
35991  * Fork - LGPL
35992  * <script type="text/javascript">
35993  */
35994  
35995 /**
35996  * @class Roo.grid.EditorGrid
35997  * @extends Roo.grid.Grid
35998  * Class for creating and editable grid.
35999  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
36000  * The container MUST have some type of size defined for the grid to fill. The container will be 
36001  * automatically set to position relative if it isn't already.
36002  * @param {Object} dataSource The data model to bind to
36003  * @param {Object} colModel The column model with info about this grid's columns
36004  */
36005 Roo.grid.EditorGrid = function(container, config){
36006     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
36007     this.getGridEl().addClass("xedit-grid");
36008
36009     if(!this.selModel){
36010         this.selModel = new Roo.grid.CellSelectionModel();
36011     }
36012
36013     this.activeEditor = null;
36014
36015         this.addEvents({
36016             /**
36017              * @event beforeedit
36018              * Fires before cell editing is triggered. The edit event object has the following properties <br />
36019              * <ul style="padding:5px;padding-left:16px;">
36020              * <li>grid - This grid</li>
36021              * <li>record - The record being edited</li>
36022              * <li>field - The field name being edited</li>
36023              * <li>value - The value for the field being edited.</li>
36024              * <li>row - The grid row index</li>
36025              * <li>column - The grid column index</li>
36026              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36027              * </ul>
36028              * @param {Object} e An edit event (see above for description)
36029              */
36030             "beforeedit" : true,
36031             /**
36032              * @event afteredit
36033              * Fires after a cell is edited. <br />
36034              * <ul style="padding:5px;padding-left:16px;">
36035              * <li>grid - This grid</li>
36036              * <li>record - The record being edited</li>
36037              * <li>field - The field name being edited</li>
36038              * <li>value - The value being set</li>
36039              * <li>originalValue - The original value for the field, before the edit.</li>
36040              * <li>row - The grid row index</li>
36041              * <li>column - The grid column index</li>
36042              * </ul>
36043              * @param {Object} e An edit event (see above for description)
36044              */
36045             "afteredit" : true,
36046             /**
36047              * @event validateedit
36048              * Fires after a cell is edited, but before the value is set in the record. 
36049          * You can use this to modify the value being set in the field, Return false
36050              * to cancel the change. The edit event object has the following properties <br />
36051              * <ul style="padding:5px;padding-left:16px;">
36052          * <li>editor - This editor</li>
36053              * <li>grid - This grid</li>
36054              * <li>record - The record being edited</li>
36055              * <li>field - The field name being edited</li>
36056              * <li>value - The value being set</li>
36057              * <li>originalValue - The original value for the field, before the edit.</li>
36058              * <li>row - The grid row index</li>
36059              * <li>column - The grid column index</li>
36060              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36061              * </ul>
36062              * @param {Object} e An edit event (see above for description)
36063              */
36064             "validateedit" : true
36065         });
36066     this.on("bodyscroll", this.stopEditing,  this);
36067     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
36068 };
36069
36070 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
36071     /**
36072      * @cfg {Number} clicksToEdit
36073      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
36074      */
36075     clicksToEdit: 2,
36076
36077     // private
36078     isEditor : true,
36079     // private
36080     trackMouseOver: false, // causes very odd FF errors
36081
36082     onCellDblClick : function(g, row, col){
36083         this.startEditing(row, col);
36084     },
36085
36086     onEditComplete : function(ed, value, startValue){
36087         this.editing = false;
36088         this.activeEditor = null;
36089         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
36090         var r = ed.record;
36091         var field = this.colModel.getDataIndex(ed.col);
36092         var e = {
36093             grid: this,
36094             record: r,
36095             field: field,
36096             originalValue: startValue,
36097             value: value,
36098             row: ed.row,
36099             column: ed.col,
36100             cancel:false,
36101             editor: ed
36102         };
36103         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
36104         cell.show();
36105           
36106         if(String(value) !== String(startValue)){
36107             
36108             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
36109                 r.set(field, e.value);
36110                 // if we are dealing with a combo box..
36111                 // then we also set the 'name' colum to be the displayField
36112                 if (ed.field.displayField && ed.field.name) {
36113                     r.set(ed.field.name, ed.field.el.dom.value);
36114                 }
36115                 
36116                 delete e.cancel; //?? why!!!
36117                 this.fireEvent("afteredit", e);
36118             }
36119         } else {
36120             this.fireEvent("afteredit", e); // always fire it!
36121         }
36122         this.view.focusCell(ed.row, ed.col);
36123     },
36124
36125     /**
36126      * Starts editing the specified for the specified row/column
36127      * @param {Number} rowIndex
36128      * @param {Number} colIndex
36129      */
36130     startEditing : function(row, col){
36131         this.stopEditing();
36132         if(this.colModel.isCellEditable(col, row)){
36133             this.view.ensureVisible(row, col, true);
36134           
36135             var r = this.dataSource.getAt(row);
36136             var field = this.colModel.getDataIndex(col);
36137             var cell = Roo.get(this.view.getCell(row,col));
36138             var e = {
36139                 grid: this,
36140                 record: r,
36141                 field: field,
36142                 value: r.data[field],
36143                 row: row,
36144                 column: col,
36145                 cancel:false 
36146             };
36147             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
36148                 this.editing = true;
36149                 var ed = this.colModel.getCellEditor(col, row);
36150                 
36151                 if (!ed) {
36152                     return;
36153                 }
36154                 if(!ed.rendered){
36155                     ed.render(ed.parentEl || document.body);
36156                 }
36157                 ed.field.reset();
36158                
36159                 cell.hide();
36160                 
36161                 (function(){ // complex but required for focus issues in safari, ie and opera
36162                     ed.row = row;
36163                     ed.col = col;
36164                     ed.record = r;
36165                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
36166                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
36167                     this.activeEditor = ed;
36168                     var v = r.data[field];
36169                     ed.startEdit(this.view.getCell(row, col), v);
36170                     // combo's with 'displayField and name set
36171                     if (ed.field.displayField && ed.field.name) {
36172                         ed.field.el.dom.value = r.data[ed.field.name];
36173                     }
36174                     
36175                     
36176                 }).defer(50, this);
36177             }
36178         }
36179     },
36180         
36181     /**
36182      * Stops any active editing
36183      */
36184     stopEditing : function(){
36185         if(this.activeEditor){
36186             this.activeEditor.completeEdit();
36187         }
36188         this.activeEditor = null;
36189     },
36190         
36191          /**
36192      * Called to get grid's drag proxy text, by default returns this.ddText.
36193      * @return {String}
36194      */
36195     getDragDropText : function(){
36196         var count = this.selModel.getSelectedCell() ? 1 : 0;
36197         return String.format(this.ddText, count, count == 1 ? '' : 's');
36198     }
36199         
36200 });/*
36201  * Based on:
36202  * Ext JS Library 1.1.1
36203  * Copyright(c) 2006-2007, Ext JS, LLC.
36204  *
36205  * Originally Released Under LGPL - original licence link has changed is not relivant.
36206  *
36207  * Fork - LGPL
36208  * <script type="text/javascript">
36209  */
36210
36211 // private - not really -- you end up using it !
36212 // This is a support class used internally by the Grid components
36213
36214 /**
36215  * @class Roo.grid.GridEditor
36216  * @extends Roo.Editor
36217  * Class for creating and editable grid elements.
36218  * @param {Object} config any settings (must include field)
36219  */
36220 Roo.grid.GridEditor = function(field, config){
36221     if (!config && field.field) {
36222         config = field;
36223         field = Roo.factory(config.field, Roo.form);
36224     }
36225     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
36226     field.monitorTab = false;
36227 };
36228
36229 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
36230     
36231     /**
36232      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
36233      */
36234     
36235     alignment: "tl-tl",
36236     autoSize: "width",
36237     hideEl : false,
36238     cls: "x-small-editor x-grid-editor",
36239     shim:false,
36240     shadow:"frame"
36241 });/*
36242  * Based on:
36243  * Ext JS Library 1.1.1
36244  * Copyright(c) 2006-2007, Ext JS, LLC.
36245  *
36246  * Originally Released Under LGPL - original licence link has changed is not relivant.
36247  *
36248  * Fork - LGPL
36249  * <script type="text/javascript">
36250  */
36251   
36252
36253   
36254 Roo.grid.PropertyRecord = Roo.data.Record.create([
36255     {name:'name',type:'string'},  'value'
36256 ]);
36257
36258
36259 Roo.grid.PropertyStore = function(grid, source){
36260     this.grid = grid;
36261     this.store = new Roo.data.Store({
36262         recordType : Roo.grid.PropertyRecord
36263     });
36264     this.store.on('update', this.onUpdate,  this);
36265     if(source){
36266         this.setSource(source);
36267     }
36268     Roo.grid.PropertyStore.superclass.constructor.call(this);
36269 };
36270
36271
36272
36273 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
36274     setSource : function(o){
36275         this.source = o;
36276         this.store.removeAll();
36277         var data = [];
36278         for(var k in o){
36279             if(this.isEditableValue(o[k])){
36280                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
36281             }
36282         }
36283         this.store.loadRecords({records: data}, {}, true);
36284     },
36285
36286     onUpdate : function(ds, record, type){
36287         if(type == Roo.data.Record.EDIT){
36288             var v = record.data['value'];
36289             var oldValue = record.modified['value'];
36290             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
36291                 this.source[record.id] = v;
36292                 record.commit();
36293                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
36294             }else{
36295                 record.reject();
36296             }
36297         }
36298     },
36299
36300     getProperty : function(row){
36301        return this.store.getAt(row);
36302     },
36303
36304     isEditableValue: function(val){
36305         if(val && val instanceof Date){
36306             return true;
36307         }else if(typeof val == 'object' || typeof val == 'function'){
36308             return false;
36309         }
36310         return true;
36311     },
36312
36313     setValue : function(prop, value){
36314         this.source[prop] = value;
36315         this.store.getById(prop).set('value', value);
36316     },
36317
36318     getSource : function(){
36319         return this.source;
36320     }
36321 });
36322
36323 Roo.grid.PropertyColumnModel = function(grid, store){
36324     this.grid = grid;
36325     var g = Roo.grid;
36326     g.PropertyColumnModel.superclass.constructor.call(this, [
36327         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
36328         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
36329     ]);
36330     this.store = store;
36331     this.bselect = Roo.DomHelper.append(document.body, {
36332         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
36333             {tag: 'option', value: 'true', html: 'true'},
36334             {tag: 'option', value: 'false', html: 'false'}
36335         ]
36336     });
36337     Roo.id(this.bselect);
36338     var f = Roo.form;
36339     this.editors = {
36340         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
36341         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
36342         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
36343         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
36344         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
36345     };
36346     this.renderCellDelegate = this.renderCell.createDelegate(this);
36347     this.renderPropDelegate = this.renderProp.createDelegate(this);
36348 };
36349
36350 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
36351     
36352     
36353     nameText : 'Name',
36354     valueText : 'Value',
36355     
36356     dateFormat : 'm/j/Y',
36357     
36358     
36359     renderDate : function(dateVal){
36360         return dateVal.dateFormat(this.dateFormat);
36361     },
36362
36363     renderBool : function(bVal){
36364         return bVal ? 'true' : 'false';
36365     },
36366
36367     isCellEditable : function(colIndex, rowIndex){
36368         return colIndex == 1;
36369     },
36370
36371     getRenderer : function(col){
36372         return col == 1 ?
36373             this.renderCellDelegate : this.renderPropDelegate;
36374     },
36375
36376     renderProp : function(v){
36377         return this.getPropertyName(v);
36378     },
36379
36380     renderCell : function(val){
36381         var rv = val;
36382         if(val instanceof Date){
36383             rv = this.renderDate(val);
36384         }else if(typeof val == 'boolean'){
36385             rv = this.renderBool(val);
36386         }
36387         return Roo.util.Format.htmlEncode(rv);
36388     },
36389
36390     getPropertyName : function(name){
36391         var pn = this.grid.propertyNames;
36392         return pn && pn[name] ? pn[name] : name;
36393     },
36394
36395     getCellEditor : function(colIndex, rowIndex){
36396         var p = this.store.getProperty(rowIndex);
36397         var n = p.data['name'], val = p.data['value'];
36398         
36399         if(typeof(this.grid.customEditors[n]) == 'string'){
36400             return this.editors[this.grid.customEditors[n]];
36401         }
36402         if(typeof(this.grid.customEditors[n]) != 'undefined'){
36403             return this.grid.customEditors[n];
36404         }
36405         if(val instanceof Date){
36406             return this.editors['date'];
36407         }else if(typeof val == 'number'){
36408             return this.editors['number'];
36409         }else if(typeof val == 'boolean'){
36410             return this.editors['boolean'];
36411         }else{
36412             return this.editors['string'];
36413         }
36414     }
36415 });
36416
36417 /**
36418  * @class Roo.grid.PropertyGrid
36419  * @extends Roo.grid.EditorGrid
36420  * This class represents the  interface of a component based property grid control.
36421  * <br><br>Usage:<pre><code>
36422  var grid = new Roo.grid.PropertyGrid("my-container-id", {
36423       
36424  });
36425  // set any options
36426  grid.render();
36427  * </code></pre>
36428   
36429  * @constructor
36430  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
36431  * The container MUST have some type of size defined for the grid to fill. The container will be
36432  * automatically set to position relative if it isn't already.
36433  * @param {Object} config A config object that sets properties on this grid.
36434  */
36435 Roo.grid.PropertyGrid = function(container, config){
36436     config = config || {};
36437     var store = new Roo.grid.PropertyStore(this);
36438     this.store = store;
36439     var cm = new Roo.grid.PropertyColumnModel(this, store);
36440     store.store.sort('name', 'ASC');
36441     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
36442         ds: store.store,
36443         cm: cm,
36444         enableColLock:false,
36445         enableColumnMove:false,
36446         stripeRows:false,
36447         trackMouseOver: false,
36448         clicksToEdit:1
36449     }, config));
36450     this.getGridEl().addClass('x-props-grid');
36451     this.lastEditRow = null;
36452     this.on('columnresize', this.onColumnResize, this);
36453     this.addEvents({
36454          /**
36455              * @event beforepropertychange
36456              * Fires before a property changes (return false to stop?)
36457              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
36458              * @param {String} id Record Id
36459              * @param {String} newval New Value
36460          * @param {String} oldval Old Value
36461              */
36462         "beforepropertychange": true,
36463         /**
36464              * @event propertychange
36465              * Fires after a property changes
36466              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
36467              * @param {String} id Record Id
36468              * @param {String} newval New Value
36469          * @param {String} oldval Old Value
36470              */
36471         "propertychange": true
36472     });
36473     this.customEditors = this.customEditors || {};
36474 };
36475 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
36476     
36477      /**
36478      * @cfg {Object} customEditors map of colnames=> custom editors.
36479      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
36480      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
36481      * false disables editing of the field.
36482          */
36483     
36484       /**
36485      * @cfg {Object} propertyNames map of property Names to their displayed value
36486          */
36487     
36488     render : function(){
36489         Roo.grid.PropertyGrid.superclass.render.call(this);
36490         this.autoSize.defer(100, this);
36491     },
36492
36493     autoSize : function(){
36494         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
36495         if(this.view){
36496             this.view.fitColumns();
36497         }
36498     },
36499
36500     onColumnResize : function(){
36501         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
36502         this.autoSize();
36503     },
36504     /**
36505      * Sets the data for the Grid
36506      * accepts a Key => Value object of all the elements avaiable.
36507      * @param {Object} data  to appear in grid.
36508      */
36509     setSource : function(source){
36510         this.store.setSource(source);
36511         //this.autoSize();
36512     },
36513     /**
36514      * Gets all the data from the grid.
36515      * @return {Object} data  data stored in grid
36516      */
36517     getSource : function(){
36518         return this.store.getSource();
36519     }
36520 });/*
36521   
36522  * Licence LGPL
36523  
36524  */
36525  
36526 /**
36527  * @class Roo.grid.Calendar
36528  * @extends Roo.util.Grid
36529  * This class extends the Grid to provide a calendar widget
36530  * <br><br>Usage:<pre><code>
36531  var grid = new Roo.grid.Calendar("my-container-id", {
36532      ds: myDataStore,
36533      cm: myColModel,
36534      selModel: mySelectionModel,
36535      autoSizeColumns: true,
36536      monitorWindowResize: false,
36537      trackMouseOver: true
36538      eventstore : real data store..
36539  });
36540  // set any options
36541  grid.render();
36542   
36543   * @constructor
36544  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
36545  * The container MUST have some type of size defined for the grid to fill. The container will be
36546  * automatically set to position relative if it isn't already.
36547  * @param {Object} config A config object that sets properties on this grid.
36548  */
36549 Roo.grid.Calendar = function(container, config){
36550         // initialize the container
36551         this.container = Roo.get(container);
36552         this.container.update("");
36553         this.container.setStyle("overflow", "hidden");
36554     this.container.addClass('x-grid-container');
36555
36556     this.id = this.container.id;
36557
36558     Roo.apply(this, config);
36559     // check and correct shorthanded configs
36560     
36561     var rows = [];
36562     var d =1;
36563     for (var r = 0;r < 6;r++) {
36564         
36565         rows[r]=[];
36566         for (var c =0;c < 7;c++) {
36567             rows[r][c]= '';
36568         }
36569     }
36570     if (this.eventStore) {
36571         this.eventStore= Roo.factory(this.eventStore, Roo.data);
36572         this.eventStore.on('load',this.onLoad, this);
36573         this.eventStore.on('beforeload',this.clearEvents, this);
36574          
36575     }
36576     
36577     this.dataSource = new Roo.data.Store({
36578             proxy: new Roo.data.MemoryProxy(rows),
36579             reader: new Roo.data.ArrayReader({}, [
36580                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
36581     });
36582
36583     this.dataSource.load();
36584     this.ds = this.dataSource;
36585     this.ds.xmodule = this.xmodule || false;
36586     
36587     
36588     var cellRender = function(v,x,r)
36589     {
36590         return String.format(
36591             '<div class="fc-day  fc-widget-content"><div>' +
36592                 '<div class="fc-event-container"></div>' +
36593                 '<div class="fc-day-number">{0}</div>'+
36594                 
36595                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
36596             '</div></div>', v);
36597     
36598     }
36599     
36600     
36601     this.colModel = new Roo.grid.ColumnModel( [
36602         {
36603             xtype: 'ColumnModel',
36604             xns: Roo.grid,
36605             dataIndex : 'weekday0',
36606             header : 'Sunday',
36607             renderer : cellRender
36608         },
36609         {
36610             xtype: 'ColumnModel',
36611             xns: Roo.grid,
36612             dataIndex : 'weekday1',
36613             header : 'Monday',
36614             renderer : cellRender
36615         },
36616         {
36617             xtype: 'ColumnModel',
36618             xns: Roo.grid,
36619             dataIndex : 'weekday2',
36620             header : 'Tuesday',
36621             renderer : cellRender
36622         },
36623         {
36624             xtype: 'ColumnModel',
36625             xns: Roo.grid,
36626             dataIndex : 'weekday3',
36627             header : 'Wednesday',
36628             renderer : cellRender
36629         },
36630         {
36631             xtype: 'ColumnModel',
36632             xns: Roo.grid,
36633             dataIndex : 'weekday4',
36634             header : 'Thursday',
36635             renderer : cellRender
36636         },
36637         {
36638             xtype: 'ColumnModel',
36639             xns: Roo.grid,
36640             dataIndex : 'weekday5',
36641             header : 'Friday',
36642             renderer : cellRender
36643         },
36644         {
36645             xtype: 'ColumnModel',
36646             xns: Roo.grid,
36647             dataIndex : 'weekday6',
36648             header : 'Saturday',
36649             renderer : cellRender
36650         }
36651     ]);
36652     this.cm = this.colModel;
36653     this.cm.xmodule = this.xmodule || false;
36654  
36655         
36656           
36657     //this.selModel = new Roo.grid.CellSelectionModel();
36658     //this.sm = this.selModel;
36659     //this.selModel.init(this);
36660     
36661     
36662     if(this.width){
36663         this.container.setWidth(this.width);
36664     }
36665
36666     if(this.height){
36667         this.container.setHeight(this.height);
36668     }
36669     /** @private */
36670         this.addEvents({
36671         // raw events
36672         /**
36673          * @event click
36674          * The raw click event for the entire grid.
36675          * @param {Roo.EventObject} e
36676          */
36677         "click" : true,
36678         /**
36679          * @event dblclick
36680          * The raw dblclick event for the entire grid.
36681          * @param {Roo.EventObject} e
36682          */
36683         "dblclick" : true,
36684         /**
36685          * @event contextmenu
36686          * The raw contextmenu event for the entire grid.
36687          * @param {Roo.EventObject} e
36688          */
36689         "contextmenu" : true,
36690         /**
36691          * @event mousedown
36692          * The raw mousedown event for the entire grid.
36693          * @param {Roo.EventObject} e
36694          */
36695         "mousedown" : true,
36696         /**
36697          * @event mouseup
36698          * The raw mouseup event for the entire grid.
36699          * @param {Roo.EventObject} e
36700          */
36701         "mouseup" : true,
36702         /**
36703          * @event mouseover
36704          * The raw mouseover event for the entire grid.
36705          * @param {Roo.EventObject} e
36706          */
36707         "mouseover" : true,
36708         /**
36709          * @event mouseout
36710          * The raw mouseout event for the entire grid.
36711          * @param {Roo.EventObject} e
36712          */
36713         "mouseout" : true,
36714         /**
36715          * @event keypress
36716          * The raw keypress event for the entire grid.
36717          * @param {Roo.EventObject} e
36718          */
36719         "keypress" : true,
36720         /**
36721          * @event keydown
36722          * The raw keydown event for the entire grid.
36723          * @param {Roo.EventObject} e
36724          */
36725         "keydown" : true,
36726
36727         // custom events
36728
36729         /**
36730          * @event cellclick
36731          * Fires when a cell is clicked
36732          * @param {Grid} this
36733          * @param {Number} rowIndex
36734          * @param {Number} columnIndex
36735          * @param {Roo.EventObject} e
36736          */
36737         "cellclick" : true,
36738         /**
36739          * @event celldblclick
36740          * Fires when a cell is double clicked
36741          * @param {Grid} this
36742          * @param {Number} rowIndex
36743          * @param {Number} columnIndex
36744          * @param {Roo.EventObject} e
36745          */
36746         "celldblclick" : true,
36747         /**
36748          * @event rowclick
36749          * Fires when a row is clicked
36750          * @param {Grid} this
36751          * @param {Number} rowIndex
36752          * @param {Roo.EventObject} e
36753          */
36754         "rowclick" : true,
36755         /**
36756          * @event rowdblclick
36757          * Fires when a row is double clicked
36758          * @param {Grid} this
36759          * @param {Number} rowIndex
36760          * @param {Roo.EventObject} e
36761          */
36762         "rowdblclick" : true,
36763         /**
36764          * @event headerclick
36765          * Fires when a header is clicked
36766          * @param {Grid} this
36767          * @param {Number} columnIndex
36768          * @param {Roo.EventObject} e
36769          */
36770         "headerclick" : true,
36771         /**
36772          * @event headerdblclick
36773          * Fires when a header cell is double clicked
36774          * @param {Grid} this
36775          * @param {Number} columnIndex
36776          * @param {Roo.EventObject} e
36777          */
36778         "headerdblclick" : true,
36779         /**
36780          * @event rowcontextmenu
36781          * Fires when a row is right clicked
36782          * @param {Grid} this
36783          * @param {Number} rowIndex
36784          * @param {Roo.EventObject} e
36785          */
36786         "rowcontextmenu" : true,
36787         /**
36788          * @event cellcontextmenu
36789          * Fires when a cell is right clicked
36790          * @param {Grid} this
36791          * @param {Number} rowIndex
36792          * @param {Number} cellIndex
36793          * @param {Roo.EventObject} e
36794          */
36795          "cellcontextmenu" : true,
36796         /**
36797          * @event headercontextmenu
36798          * Fires when a header is right clicked
36799          * @param {Grid} this
36800          * @param {Number} columnIndex
36801          * @param {Roo.EventObject} e
36802          */
36803         "headercontextmenu" : true,
36804         /**
36805          * @event bodyscroll
36806          * Fires when the body element is scrolled
36807          * @param {Number} scrollLeft
36808          * @param {Number} scrollTop
36809          */
36810         "bodyscroll" : true,
36811         /**
36812          * @event columnresize
36813          * Fires when the user resizes a column
36814          * @param {Number} columnIndex
36815          * @param {Number} newSize
36816          */
36817         "columnresize" : true,
36818         /**
36819          * @event columnmove
36820          * Fires when the user moves a column
36821          * @param {Number} oldIndex
36822          * @param {Number} newIndex
36823          */
36824         "columnmove" : true,
36825         /**
36826          * @event startdrag
36827          * Fires when row(s) start being dragged
36828          * @param {Grid} this
36829          * @param {Roo.GridDD} dd The drag drop object
36830          * @param {event} e The raw browser event
36831          */
36832         "startdrag" : true,
36833         /**
36834          * @event enddrag
36835          * Fires when a drag operation is complete
36836          * @param {Grid} this
36837          * @param {Roo.GridDD} dd The drag drop object
36838          * @param {event} e The raw browser event
36839          */
36840         "enddrag" : true,
36841         /**
36842          * @event dragdrop
36843          * Fires when dragged row(s) are dropped on a valid DD target
36844          * @param {Grid} this
36845          * @param {Roo.GridDD} dd The drag drop object
36846          * @param {String} targetId The target drag drop object
36847          * @param {event} e The raw browser event
36848          */
36849         "dragdrop" : true,
36850         /**
36851          * @event dragover
36852          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
36853          * @param {Grid} this
36854          * @param {Roo.GridDD} dd The drag drop object
36855          * @param {String} targetId The target drag drop object
36856          * @param {event} e The raw browser event
36857          */
36858         "dragover" : true,
36859         /**
36860          * @event dragenter
36861          *  Fires when the dragged row(s) first cross another DD target while being dragged
36862          * @param {Grid} this
36863          * @param {Roo.GridDD} dd The drag drop object
36864          * @param {String} targetId The target drag drop object
36865          * @param {event} e The raw browser event
36866          */
36867         "dragenter" : true,
36868         /**
36869          * @event dragout
36870          * Fires when the dragged row(s) leave another DD target while being dragged
36871          * @param {Grid} this
36872          * @param {Roo.GridDD} dd The drag drop object
36873          * @param {String} targetId The target drag drop object
36874          * @param {event} e The raw browser event
36875          */
36876         "dragout" : true,
36877         /**
36878          * @event rowclass
36879          * Fires when a row is rendered, so you can change add a style to it.
36880          * @param {GridView} gridview   The grid view
36881          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
36882          */
36883         'rowclass' : true,
36884
36885         /**
36886          * @event render
36887          * Fires when the grid is rendered
36888          * @param {Grid} grid
36889          */
36890         'render' : true,
36891             /**
36892              * @event select
36893              * Fires when a date is selected
36894              * @param {DatePicker} this
36895              * @param {Date} date The selected date
36896              */
36897         'select': true,
36898         /**
36899              * @event monthchange
36900              * Fires when the displayed month changes 
36901              * @param {DatePicker} this
36902              * @param {Date} date The selected month
36903              */
36904         'monthchange': true,
36905         /**
36906              * @event evententer
36907              * Fires when mouse over an event
36908              * @param {Calendar} this
36909              * @param {event} Event
36910              */
36911         'evententer': true,
36912         /**
36913              * @event eventleave
36914              * Fires when the mouse leaves an
36915              * @param {Calendar} this
36916              * @param {event}
36917              */
36918         'eventleave': true,
36919         /**
36920              * @event eventclick
36921              * Fires when the mouse click an
36922              * @param {Calendar} this
36923              * @param {event}
36924              */
36925         'eventclick': true,
36926         /**
36927              * @event eventrender
36928              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
36929              * @param {Calendar} this
36930              * @param {data} data to be modified
36931              */
36932         'eventrender': true
36933         
36934     });
36935
36936     Roo.grid.Grid.superclass.constructor.call(this);
36937     this.on('render', function() {
36938         this.view.el.addClass('x-grid-cal'); 
36939         
36940         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
36941
36942     },this);
36943     
36944     if (!Roo.grid.Calendar.style) {
36945         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
36946             
36947             
36948             '.x-grid-cal .x-grid-col' :  {
36949                 height: 'auto !important',
36950                 'vertical-align': 'top'
36951             },
36952             '.x-grid-cal  .fc-event-hori' : {
36953                 height: '14px'
36954             }
36955              
36956             
36957         }, Roo.id());
36958     }
36959
36960     
36961     
36962 };
36963 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
36964     /**
36965      * @cfg {Store} eventStore The store that loads events.
36966      */
36967     eventStore : 25,
36968
36969      
36970     activeDate : false,
36971     startDay : 0,
36972     autoWidth : true,
36973     monitorWindowResize : false,
36974
36975     
36976     resizeColumns : function() {
36977         var col = (this.view.el.getWidth() / 7) - 3;
36978         // loop through cols, and setWidth
36979         for(var i =0 ; i < 7 ; i++){
36980             this.cm.setColumnWidth(i, col);
36981         }
36982     },
36983      setDate :function(date) {
36984         
36985         Roo.log('setDate?');
36986         
36987         this.resizeColumns();
36988         var vd = this.activeDate;
36989         this.activeDate = date;
36990 //        if(vd && this.el){
36991 //            var t = date.getTime();
36992 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
36993 //                Roo.log('using add remove');
36994 //                
36995 //                this.fireEvent('monthchange', this, date);
36996 //                
36997 //                this.cells.removeClass("fc-state-highlight");
36998 //                this.cells.each(function(c){
36999 //                   if(c.dateValue == t){
37000 //                       c.addClass("fc-state-highlight");
37001 //                       setTimeout(function(){
37002 //                            try{c.dom.firstChild.focus();}catch(e){}
37003 //                       }, 50);
37004 //                       return false;
37005 //                   }
37006 //                   return true;
37007 //                });
37008 //                return;
37009 //            }
37010 //        }
37011         
37012         var days = date.getDaysInMonth();
37013         
37014         var firstOfMonth = date.getFirstDateOfMonth();
37015         var startingPos = firstOfMonth.getDay()-this.startDay;
37016         
37017         if(startingPos < this.startDay){
37018             startingPos += 7;
37019         }
37020         
37021         var pm = date.add(Date.MONTH, -1);
37022         var prevStart = pm.getDaysInMonth()-startingPos;
37023 //        
37024         
37025         
37026         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37027         
37028         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
37029         //this.cells.addClassOnOver('fc-state-hover');
37030         
37031         var cells = this.cells.elements;
37032         var textEls = this.textNodes;
37033         
37034         //Roo.each(cells, function(cell){
37035         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
37036         //});
37037         
37038         days += startingPos;
37039
37040         // convert everything to numbers so it's fast
37041         var day = 86400000;
37042         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
37043         //Roo.log(d);
37044         //Roo.log(pm);
37045         //Roo.log(prevStart);
37046         
37047         var today = new Date().clearTime().getTime();
37048         var sel = date.clearTime().getTime();
37049         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
37050         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
37051         var ddMatch = this.disabledDatesRE;
37052         var ddText = this.disabledDatesText;
37053         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
37054         var ddaysText = this.disabledDaysText;
37055         var format = this.format;
37056         
37057         var setCellClass = function(cal, cell){
37058             
37059             //Roo.log('set Cell Class');
37060             cell.title = "";
37061             var t = d.getTime();
37062             
37063             //Roo.log(d);
37064             
37065             
37066             cell.dateValue = t;
37067             if(t == today){
37068                 cell.className += " fc-today";
37069                 cell.className += " fc-state-highlight";
37070                 cell.title = cal.todayText;
37071             }
37072             if(t == sel){
37073                 // disable highlight in other month..
37074                 cell.className += " fc-state-highlight";
37075                 
37076             }
37077             // disabling
37078             if(t < min) {
37079                 //cell.className = " fc-state-disabled";
37080                 cell.title = cal.minText;
37081                 return;
37082             }
37083             if(t > max) {
37084                 //cell.className = " fc-state-disabled";
37085                 cell.title = cal.maxText;
37086                 return;
37087             }
37088             if(ddays){
37089                 if(ddays.indexOf(d.getDay()) != -1){
37090                     // cell.title = ddaysText;
37091                    // cell.className = " fc-state-disabled";
37092                 }
37093             }
37094             if(ddMatch && format){
37095                 var fvalue = d.dateFormat(format);
37096                 if(ddMatch.test(fvalue)){
37097                     cell.title = ddText.replace("%0", fvalue);
37098                    cell.className = " fc-state-disabled";
37099                 }
37100             }
37101             
37102             if (!cell.initialClassName) {
37103                 cell.initialClassName = cell.dom.className;
37104             }
37105             
37106             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
37107         };
37108
37109         var i = 0;
37110         
37111         for(; i < startingPos; i++) {
37112             cells[i].dayName =  (++prevStart);
37113             Roo.log(textEls[i]);
37114             d.setDate(d.getDate()+1);
37115             
37116             //cells[i].className = "fc-past fc-other-month";
37117             setCellClass(this, cells[i]);
37118         }
37119         
37120         var intDay = 0;
37121         
37122         for(; i < days; i++){
37123             intDay = i - startingPos + 1;
37124             cells[i].dayName =  (intDay);
37125             d.setDate(d.getDate()+1);
37126             
37127             cells[i].className = ''; // "x-date-active";
37128             setCellClass(this, cells[i]);
37129         }
37130         var extraDays = 0;
37131         
37132         for(; i < 42; i++) {
37133             //textEls[i].innerHTML = (++extraDays);
37134             
37135             d.setDate(d.getDate()+1);
37136             cells[i].dayName = (++extraDays);
37137             cells[i].className = "fc-future fc-other-month";
37138             setCellClass(this, cells[i]);
37139         }
37140         
37141         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
37142         
37143         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
37144         
37145         // this will cause all the cells to mis
37146         var rows= [];
37147         var i =0;
37148         for (var r = 0;r < 6;r++) {
37149             for (var c =0;c < 7;c++) {
37150                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
37151             }    
37152         }
37153         
37154         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37155         for(i=0;i<cells.length;i++) {
37156             
37157             this.cells.elements[i].dayName = cells[i].dayName ;
37158             this.cells.elements[i].className = cells[i].className;
37159             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
37160             this.cells.elements[i].title = cells[i].title ;
37161             this.cells.elements[i].dateValue = cells[i].dateValue ;
37162         }
37163         
37164         
37165         
37166         
37167         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
37168         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
37169         
37170         ////if(totalRows != 6){
37171             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
37172            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
37173        // }
37174         
37175         this.fireEvent('monthchange', this, date);
37176         
37177         
37178     },
37179  /**
37180      * Returns the grid's SelectionModel.
37181      * @return {SelectionModel}
37182      */
37183     getSelectionModel : function(){
37184         if(!this.selModel){
37185             this.selModel = new Roo.grid.CellSelectionModel();
37186         }
37187         return this.selModel;
37188     },
37189
37190     load: function() {
37191         this.eventStore.load()
37192         
37193         
37194         
37195     },
37196     
37197     findCell : function(dt) {
37198         dt = dt.clearTime().getTime();
37199         var ret = false;
37200         this.cells.each(function(c){
37201             //Roo.log("check " +c.dateValue + '?=' + dt);
37202             if(c.dateValue == dt){
37203                 ret = c;
37204                 return false;
37205             }
37206             return true;
37207         });
37208         
37209         return ret;
37210     },
37211     
37212     findCells : function(rec) {
37213         var s = rec.data.start_dt.clone().clearTime().getTime();
37214        // Roo.log(s);
37215         var e= rec.data.end_dt.clone().clearTime().getTime();
37216        // Roo.log(e);
37217         var ret = [];
37218         this.cells.each(function(c){
37219              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
37220             
37221             if(c.dateValue > e){
37222                 return ;
37223             }
37224             if(c.dateValue < s){
37225                 return ;
37226             }
37227             ret.push(c);
37228         });
37229         
37230         return ret;    
37231     },
37232     
37233     findBestRow: function(cells)
37234     {
37235         var ret = 0;
37236         
37237         for (var i =0 ; i < cells.length;i++) {
37238             ret  = Math.max(cells[i].rows || 0,ret);
37239         }
37240         return ret;
37241         
37242     },
37243     
37244     
37245     addItem : function(rec)
37246     {
37247         // look for vertical location slot in
37248         var cells = this.findCells(rec);
37249         
37250         rec.row = this.findBestRow(cells);
37251         
37252         // work out the location.
37253         
37254         var crow = false;
37255         var rows = [];
37256         for(var i =0; i < cells.length; i++) {
37257             if (!crow) {
37258                 crow = {
37259                     start : cells[i],
37260                     end :  cells[i]
37261                 };
37262                 continue;
37263             }
37264             if (crow.start.getY() == cells[i].getY()) {
37265                 // on same row.
37266                 crow.end = cells[i];
37267                 continue;
37268             }
37269             // different row.
37270             rows.push(crow);
37271             crow = {
37272                 start: cells[i],
37273                 end : cells[i]
37274             };
37275             
37276         }
37277         
37278         rows.push(crow);
37279         rec.els = [];
37280         rec.rows = rows;
37281         rec.cells = cells;
37282         for (var i = 0; i < cells.length;i++) {
37283             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
37284             
37285         }
37286         
37287         
37288     },
37289     
37290     clearEvents: function() {
37291         
37292         if (!this.eventStore.getCount()) {
37293             return;
37294         }
37295         // reset number of rows in cells.
37296         Roo.each(this.cells.elements, function(c){
37297             c.rows = 0;
37298         });
37299         
37300         this.eventStore.each(function(e) {
37301             this.clearEvent(e);
37302         },this);
37303         
37304     },
37305     
37306     clearEvent : function(ev)
37307     {
37308         if (ev.els) {
37309             Roo.each(ev.els, function(el) {
37310                 el.un('mouseenter' ,this.onEventEnter, this);
37311                 el.un('mouseleave' ,this.onEventLeave, this);
37312                 el.remove();
37313             },this);
37314             ev.els = [];
37315         }
37316     },
37317     
37318     
37319     renderEvent : function(ev,ctr) {
37320         if (!ctr) {
37321              ctr = this.view.el.select('.fc-event-container',true).first();
37322         }
37323         
37324          
37325         this.clearEvent(ev);
37326             //code
37327        
37328         
37329         
37330         ev.els = [];
37331         var cells = ev.cells;
37332         var rows = ev.rows;
37333         this.fireEvent('eventrender', this, ev);
37334         
37335         for(var i =0; i < rows.length; i++) {
37336             
37337             cls = '';
37338             if (i == 0) {
37339                 cls += ' fc-event-start';
37340             }
37341             if ((i+1) == rows.length) {
37342                 cls += ' fc-event-end';
37343             }
37344             
37345             //Roo.log(ev.data);
37346             // how many rows should it span..
37347             var cg = this.eventTmpl.append(ctr,Roo.apply({
37348                 fccls : cls
37349                 
37350             }, ev.data) , true);
37351             
37352             
37353             cg.on('mouseenter' ,this.onEventEnter, this, ev);
37354             cg.on('mouseleave' ,this.onEventLeave, this, ev);
37355             cg.on('click', this.onEventClick, this, ev);
37356             
37357             ev.els.push(cg);
37358             
37359             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
37360             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
37361             //Roo.log(cg);
37362              
37363             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
37364             cg.setWidth(ebox.right - sbox.x -2);
37365         }
37366     },
37367     
37368     renderEvents: function()
37369     {   
37370         // first make sure there is enough space..
37371         
37372         if (!this.eventTmpl) {
37373             this.eventTmpl = new Roo.Template(
37374                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
37375                     '<div class="fc-event-inner">' +
37376                         '<span class="fc-event-time">{time}</span>' +
37377                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
37378                     '</div>' +
37379                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
37380                 '</div>'
37381             );
37382                 
37383         }
37384                
37385         
37386         
37387         this.cells.each(function(c) {
37388             //Roo.log(c.select('.fc-day-content div',true).first());
37389             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
37390         });
37391         
37392         var ctr = this.view.el.select('.fc-event-container',true).first();
37393         
37394         var cls;
37395         this.eventStore.each(function(ev){
37396             
37397             this.renderEvent(ev);
37398              
37399              
37400         }, this);
37401         this.view.layout();
37402         
37403     },
37404     
37405     onEventEnter: function (e, el,event,d) {
37406         this.fireEvent('evententer', this, el, event);
37407     },
37408     
37409     onEventLeave: function (e, el,event,d) {
37410         this.fireEvent('eventleave', this, el, event);
37411     },
37412     
37413     onEventClick: function (e, el,event,d) {
37414         this.fireEvent('eventclick', this, el, event);
37415     },
37416     
37417     onMonthChange: function () {
37418         this.store.load();
37419     },
37420     
37421     onLoad: function () {
37422         
37423         //Roo.log('calendar onload');
37424 //         
37425         if(this.eventStore.getCount() > 0){
37426             
37427            
37428             
37429             this.eventStore.each(function(d){
37430                 
37431                 
37432                 // FIXME..
37433                 var add =   d.data;
37434                 if (typeof(add.end_dt) == 'undefined')  {
37435                     Roo.log("Missing End time in calendar data: ");
37436                     Roo.log(d);
37437                     return;
37438                 }
37439                 if (typeof(add.start_dt) == 'undefined')  {
37440                     Roo.log("Missing Start time in calendar data: ");
37441                     Roo.log(d);
37442                     return;
37443                 }
37444                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
37445                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
37446                 add.id = add.id || d.id;
37447                 add.title = add.title || '??';
37448                 
37449                 this.addItem(d);
37450                 
37451              
37452             },this);
37453         }
37454         
37455         this.renderEvents();
37456     }
37457     
37458
37459 });
37460 /*
37461  grid : {
37462                 xtype: 'Grid',
37463                 xns: Roo.grid,
37464                 listeners : {
37465                     render : function ()
37466                     {
37467                         _this.grid = this;
37468                         
37469                         if (!this.view.el.hasClass('course-timesheet')) {
37470                             this.view.el.addClass('course-timesheet');
37471                         }
37472                         if (this.tsStyle) {
37473                             this.ds.load({});
37474                             return; 
37475                         }
37476                         Roo.log('width');
37477                         Roo.log(_this.grid.view.el.getWidth());
37478                         
37479                         
37480                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
37481                             '.course-timesheet .x-grid-row' : {
37482                                 height: '80px'
37483                             },
37484                             '.x-grid-row td' : {
37485                                 'vertical-align' : 0
37486                             },
37487                             '.course-edit-link' : {
37488                                 'color' : 'blue',
37489                                 'text-overflow' : 'ellipsis',
37490                                 'overflow' : 'hidden',
37491                                 'white-space' : 'nowrap',
37492                                 'cursor' : 'pointer'
37493                             },
37494                             '.sub-link' : {
37495                                 'color' : 'green'
37496                             },
37497                             '.de-act-sup-link' : {
37498                                 'color' : 'purple',
37499                                 'text-decoration' : 'line-through'
37500                             },
37501                             '.de-act-link' : {
37502                                 'color' : 'red',
37503                                 'text-decoration' : 'line-through'
37504                             },
37505                             '.course-timesheet .course-highlight' : {
37506                                 'border-top-style': 'dashed !important',
37507                                 'border-bottom-bottom': 'dashed !important'
37508                             },
37509                             '.course-timesheet .course-item' : {
37510                                 'font-family'   : 'tahoma, arial, helvetica',
37511                                 'font-size'     : '11px',
37512                                 'overflow'      : 'hidden',
37513                                 'padding-left'  : '10px',
37514                                 'padding-right' : '10px',
37515                                 'padding-top' : '10px' 
37516                             }
37517                             
37518                         }, Roo.id());
37519                                 this.ds.load({});
37520                     }
37521                 },
37522                 autoWidth : true,
37523                 monitorWindowResize : false,
37524                 cellrenderer : function(v,x,r)
37525                 {
37526                     return v;
37527                 },
37528                 sm : {
37529                     xtype: 'CellSelectionModel',
37530                     xns: Roo.grid
37531                 },
37532                 dataSource : {
37533                     xtype: 'Store',
37534                     xns: Roo.data,
37535                     listeners : {
37536                         beforeload : function (_self, options)
37537                         {
37538                             options.params = options.params || {};
37539                             options.params._month = _this.monthField.getValue();
37540                             options.params.limit = 9999;
37541                             options.params['sort'] = 'when_dt';    
37542                             options.params['dir'] = 'ASC';    
37543                             this.proxy.loadResponse = this.loadResponse;
37544                             Roo.log("load?");
37545                             //this.addColumns();
37546                         },
37547                         load : function (_self, records, options)
37548                         {
37549                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
37550                                 // if you click on the translation.. you can edit it...
37551                                 var el = Roo.get(this);
37552                                 var id = el.dom.getAttribute('data-id');
37553                                 var d = el.dom.getAttribute('data-date');
37554                                 var t = el.dom.getAttribute('data-time');
37555                                 //var id = this.child('span').dom.textContent;
37556                                 
37557                                 //Roo.log(this);
37558                                 Pman.Dialog.CourseCalendar.show({
37559                                     id : id,
37560                                     when_d : d,
37561                                     when_t : t,
37562                                     productitem_active : id ? 1 : 0
37563                                 }, function() {
37564                                     _this.grid.ds.load({});
37565                                 });
37566                            
37567                            });
37568                            
37569                            _this.panel.fireEvent('resize', [ '', '' ]);
37570                         }
37571                     },
37572                     loadResponse : function(o, success, response){
37573                             // this is overridden on before load..
37574                             
37575                             Roo.log("our code?");       
37576                             //Roo.log(success);
37577                             //Roo.log(response)
37578                             delete this.activeRequest;
37579                             if(!success){
37580                                 this.fireEvent("loadexception", this, o, response);
37581                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
37582                                 return;
37583                             }
37584                             var result;
37585                             try {
37586                                 result = o.reader.read(response);
37587                             }catch(e){
37588                                 Roo.log("load exception?");
37589                                 this.fireEvent("loadexception", this, o, response, e);
37590                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
37591                                 return;
37592                             }
37593                             Roo.log("ready...");        
37594                             // loop through result.records;
37595                             // and set this.tdate[date] = [] << array of records..
37596                             _this.tdata  = {};
37597                             Roo.each(result.records, function(r){
37598                                 //Roo.log(r.data);
37599                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
37600                                     _this.tdata[r.data.when_dt.format('j')] = [];
37601                                 }
37602                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
37603                             });
37604                             
37605                             //Roo.log(_this.tdata);
37606                             
37607                             result.records = [];
37608                             result.totalRecords = 6;
37609                     
37610                             // let's generate some duumy records for the rows.
37611                             //var st = _this.dateField.getValue();
37612                             
37613                             // work out monday..
37614                             //st = st.add(Date.DAY, -1 * st.format('w'));
37615                             
37616                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
37617                             
37618                             var firstOfMonth = date.getFirstDayOfMonth();
37619                             var days = date.getDaysInMonth();
37620                             var d = 1;
37621                             var firstAdded = false;
37622                             for (var i = 0; i < result.totalRecords ; i++) {
37623                                 //var d= st.add(Date.DAY, i);
37624                                 var row = {};
37625                                 var added = 0;
37626                                 for(var w = 0 ; w < 7 ; w++){
37627                                     if(!firstAdded && firstOfMonth != w){
37628                                         continue;
37629                                     }
37630                                     if(d > days){
37631                                         continue;
37632                                     }
37633                                     firstAdded = true;
37634                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
37635                                     row['weekday'+w] = String.format(
37636                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
37637                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
37638                                                     d,
37639                                                     date.format('Y-m-')+dd
37640                                                 );
37641                                     added++;
37642                                     if(typeof(_this.tdata[d]) != 'undefined'){
37643                                         Roo.each(_this.tdata[d], function(r){
37644                                             var is_sub = '';
37645                                             var deactive = '';
37646                                             var id = r.id;
37647                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
37648                                             if(r.parent_id*1>0){
37649                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
37650                                                 id = r.parent_id;
37651                                             }
37652                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
37653                                                 deactive = 'de-act-link';
37654                                             }
37655                                             
37656                                             row['weekday'+w] += String.format(
37657                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
37658                                                     id, //0
37659                                                     r.product_id_name, //1
37660                                                     r.when_dt.format('h:ia'), //2
37661                                                     is_sub, //3
37662                                                     deactive, //4
37663                                                     desc // 5
37664                                             );
37665                                         });
37666                                     }
37667                                     d++;
37668                                 }
37669                                 
37670                                 // only do this if something added..
37671                                 if(added > 0){ 
37672                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
37673                                 }
37674                                 
37675                                 
37676                                 // push it twice. (second one with an hour..
37677                                 
37678                             }
37679                             //Roo.log(result);
37680                             this.fireEvent("load", this, o, o.request.arg);
37681                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
37682                         },
37683                     sortInfo : {field: 'when_dt', direction : 'ASC' },
37684                     proxy : {
37685                         xtype: 'HttpProxy',
37686                         xns: Roo.data,
37687                         method : 'GET',
37688                         url : baseURL + '/Roo/Shop_course.php'
37689                     },
37690                     reader : {
37691                         xtype: 'JsonReader',
37692                         xns: Roo.data,
37693                         id : 'id',
37694                         fields : [
37695                             {
37696                                 'name': 'id',
37697                                 'type': 'int'
37698                             },
37699                             {
37700                                 'name': 'when_dt',
37701                                 'type': 'string'
37702                             },
37703                             {
37704                                 'name': 'end_dt',
37705                                 'type': 'string'
37706                             },
37707                             {
37708                                 'name': 'parent_id',
37709                                 'type': 'int'
37710                             },
37711                             {
37712                                 'name': 'product_id',
37713                                 'type': 'int'
37714                             },
37715                             {
37716                                 'name': 'productitem_id',
37717                                 'type': 'int'
37718                             },
37719                             {
37720                                 'name': 'guid',
37721                                 'type': 'int'
37722                             }
37723                         ]
37724                     }
37725                 },
37726                 toolbar : {
37727                     xtype: 'Toolbar',
37728                     xns: Roo,
37729                     items : [
37730                         {
37731                             xtype: 'Button',
37732                             xns: Roo.Toolbar,
37733                             listeners : {
37734                                 click : function (_self, e)
37735                                 {
37736                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
37737                                     sd.setMonth(sd.getMonth()-1);
37738                                     _this.monthField.setValue(sd.format('Y-m-d'));
37739                                     _this.grid.ds.load({});
37740                                 }
37741                             },
37742                             text : "Back"
37743                         },
37744                         {
37745                             xtype: 'Separator',
37746                             xns: Roo.Toolbar
37747                         },
37748                         {
37749                             xtype: 'MonthField',
37750                             xns: Roo.form,
37751                             listeners : {
37752                                 render : function (_self)
37753                                 {
37754                                     _this.monthField = _self;
37755                                    // _this.monthField.set  today
37756                                 },
37757                                 select : function (combo, date)
37758                                 {
37759                                     _this.grid.ds.load({});
37760                                 }
37761                             },
37762                             value : (function() { return new Date(); })()
37763                         },
37764                         {
37765                             xtype: 'Separator',
37766                             xns: Roo.Toolbar
37767                         },
37768                         {
37769                             xtype: 'TextItem',
37770                             xns: Roo.Toolbar,
37771                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
37772                         },
37773                         {
37774                             xtype: 'Fill',
37775                             xns: Roo.Toolbar
37776                         },
37777                         {
37778                             xtype: 'Button',
37779                             xns: Roo.Toolbar,
37780                             listeners : {
37781                                 click : function (_self, e)
37782                                 {
37783                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
37784                                     sd.setMonth(sd.getMonth()+1);
37785                                     _this.monthField.setValue(sd.format('Y-m-d'));
37786                                     _this.grid.ds.load({});
37787                                 }
37788                             },
37789                             text : "Next"
37790                         }
37791                     ]
37792                 },
37793                  
37794             }
37795         };
37796         
37797         *//*
37798  * Based on:
37799  * Ext JS Library 1.1.1
37800  * Copyright(c) 2006-2007, Ext JS, LLC.
37801  *
37802  * Originally Released Under LGPL - original licence link has changed is not relivant.
37803  *
37804  * Fork - LGPL
37805  * <script type="text/javascript">
37806  */
37807  
37808 /**
37809  * @class Roo.LoadMask
37810  * A simple utility class for generically masking elements while loading data.  If the element being masked has
37811  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
37812  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
37813  * element's UpdateManager load indicator and will be destroyed after the initial load.
37814  * @constructor
37815  * Create a new LoadMask
37816  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
37817  * @param {Object} config The config object
37818  */
37819 Roo.LoadMask = function(el, config){
37820     this.el = Roo.get(el);
37821     Roo.apply(this, config);
37822     if(this.store){
37823         this.store.on('beforeload', this.onBeforeLoad, this);
37824         this.store.on('load', this.onLoad, this);
37825         this.store.on('loadexception', this.onLoadException, this);
37826         this.removeMask = false;
37827     }else{
37828         var um = this.el.getUpdateManager();
37829         um.showLoadIndicator = false; // disable the default indicator
37830         um.on('beforeupdate', this.onBeforeLoad, this);
37831         um.on('update', this.onLoad, this);
37832         um.on('failure', this.onLoad, this);
37833         this.removeMask = true;
37834     }
37835 };
37836
37837 Roo.LoadMask.prototype = {
37838     /**
37839      * @cfg {Boolean} removeMask
37840      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
37841      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
37842      */
37843     /**
37844      * @cfg {String} msg
37845      * The text to display in a centered loading message box (defaults to 'Loading...')
37846      */
37847     msg : 'Loading...',
37848     /**
37849      * @cfg {String} msgCls
37850      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
37851      */
37852     msgCls : 'x-mask-loading',
37853
37854     /**
37855      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
37856      * @type Boolean
37857      */
37858     disabled: false,
37859
37860     /**
37861      * Disables the mask to prevent it from being displayed
37862      */
37863     disable : function(){
37864        this.disabled = true;
37865     },
37866
37867     /**
37868      * Enables the mask so that it can be displayed
37869      */
37870     enable : function(){
37871         this.disabled = false;
37872     },
37873     
37874     onLoadException : function()
37875     {
37876         Roo.log(arguments);
37877         
37878         if (typeof(arguments[3]) != 'undefined') {
37879             Roo.MessageBox.alert("Error loading",arguments[3]);
37880         } 
37881         /*
37882         try {
37883             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
37884                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
37885             }   
37886         } catch(e) {
37887             
37888         }
37889         */
37890     
37891         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
37892     },
37893     // private
37894     onLoad : function()
37895     {
37896         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
37897     },
37898
37899     // private
37900     onBeforeLoad : function(){
37901         if(!this.disabled){
37902             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
37903         }
37904     },
37905
37906     // private
37907     destroy : function(){
37908         if(this.store){
37909             this.store.un('beforeload', this.onBeforeLoad, this);
37910             this.store.un('load', this.onLoad, this);
37911             this.store.un('loadexception', this.onLoadException, this);
37912         }else{
37913             var um = this.el.getUpdateManager();
37914             um.un('beforeupdate', this.onBeforeLoad, this);
37915             um.un('update', this.onLoad, this);
37916             um.un('failure', this.onLoad, this);
37917         }
37918     }
37919 };/*
37920  * Based on:
37921  * Ext JS Library 1.1.1
37922  * Copyright(c) 2006-2007, Ext JS, LLC.
37923  *
37924  * Originally Released Under LGPL - original licence link has changed is not relivant.
37925  *
37926  * Fork - LGPL
37927  * <script type="text/javascript">
37928  */
37929
37930
37931 /**
37932  * @class Roo.XTemplate
37933  * @extends Roo.Template
37934  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
37935 <pre><code>
37936 var t = new Roo.XTemplate(
37937         '&lt;select name="{name}"&gt;',
37938                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
37939         '&lt;/select&gt;'
37940 );
37941  
37942 // then append, applying the master template values
37943  </code></pre>
37944  *
37945  * Supported features:
37946  *
37947  *  Tags:
37948
37949 <pre><code>
37950       {a_variable} - output encoded.
37951       {a_variable.format:("Y-m-d")} - call a method on the variable
37952       {a_variable:raw} - unencoded output
37953       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
37954       {a_variable:this.method_on_template(...)} - call a method on the template object.
37955  
37956 </code></pre>
37957  *  The tpl tag:
37958 <pre><code>
37959         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
37960         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
37961         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
37962         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
37963   
37964         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
37965         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
37966 </code></pre>
37967  *      
37968  */
37969 Roo.XTemplate = function()
37970 {
37971     Roo.XTemplate.superclass.constructor.apply(this, arguments);
37972     if (this.html) {
37973         this.compile();
37974     }
37975 };
37976
37977
37978 Roo.extend(Roo.XTemplate, Roo.Template, {
37979
37980     /**
37981      * The various sub templates
37982      */
37983     tpls : false,
37984     /**
37985      *
37986      * basic tag replacing syntax
37987      * WORD:WORD()
37988      *
37989      * // you can fake an object call by doing this
37990      *  x.t:(test,tesT) 
37991      * 
37992      */
37993     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
37994
37995     /**
37996      * compile the template
37997      *
37998      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
37999      *
38000      */
38001     compile: function()
38002     {
38003         var s = this.html;
38004      
38005         s = ['<tpl>', s, '</tpl>'].join('');
38006     
38007         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
38008             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
38009             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
38010             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
38011             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
38012             m,
38013             id     = 0,
38014             tpls   = [];
38015     
38016         while(true == !!(m = s.match(re))){
38017             var forMatch   = m[0].match(nameRe),
38018                 ifMatch   = m[0].match(ifRe),
38019                 execMatch   = m[0].match(execRe),
38020                 namedMatch   = m[0].match(namedRe),
38021                 
38022                 exp  = null, 
38023                 fn   = null,
38024                 exec = null,
38025                 name = forMatch && forMatch[1] ? forMatch[1] : '';
38026                 
38027             if (ifMatch) {
38028                 // if - puts fn into test..
38029                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
38030                 if(exp){
38031                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
38032                 }
38033             }
38034             
38035             if (execMatch) {
38036                 // exec - calls a function... returns empty if true is  returned.
38037                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
38038                 if(exp){
38039                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
38040                 }
38041             }
38042             
38043             
38044             if (name) {
38045                 // for = 
38046                 switch(name){
38047                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
38048                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
38049                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
38050                 }
38051             }
38052             var uid = namedMatch ? namedMatch[1] : id;
38053             
38054             
38055             tpls.push({
38056                 id:     namedMatch ? namedMatch[1] : id,
38057                 target: name,
38058                 exec:   exec,
38059                 test:   fn,
38060                 body:   m[1] || ''
38061             });
38062             if (namedMatch) {
38063                 s = s.replace(m[0], '');
38064             } else { 
38065                 s = s.replace(m[0], '{xtpl'+ id + '}');
38066             }
38067             ++id;
38068         }
38069         this.tpls = [];
38070         for(var i = tpls.length-1; i >= 0; --i){
38071             this.compileTpl(tpls[i]);
38072             this.tpls[tpls[i].id] = tpls[i];
38073         }
38074         this.master = tpls[tpls.length-1];
38075         return this;
38076     },
38077     /**
38078      * same as applyTemplate, except it's done to one of the subTemplates
38079      * when using named templates, you can do:
38080      *
38081      * var str = pl.applySubTemplate('your-name', values);
38082      *
38083      * 
38084      * @param {Number} id of the template
38085      * @param {Object} values to apply to template
38086      * @param {Object} parent (normaly the instance of this object)
38087      */
38088     applySubTemplate : function(id, values, parent)
38089     {
38090         
38091         
38092         var t = this.tpls[id];
38093         
38094         
38095         try { 
38096             if(t.test && !t.test.call(this, values, parent)){
38097                 return '';
38098             }
38099         } catch(e) {
38100             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
38101             Roo.log(e.toString());
38102             Roo.log(t.test);
38103             return ''
38104         }
38105         try { 
38106             
38107             if(t.exec && t.exec.call(this, values, parent)){
38108                 return '';
38109             }
38110         } catch(e) {
38111             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
38112             Roo.log(e.toString());
38113             Roo.log(t.exec);
38114             return ''
38115         }
38116         try {
38117             var vs = t.target ? t.target.call(this, values, parent) : values;
38118             parent = t.target ? values : parent;
38119             if(t.target && vs instanceof Array){
38120                 var buf = [];
38121                 for(var i = 0, len = vs.length; i < len; i++){
38122                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
38123                 }
38124                 return buf.join('');
38125             }
38126             return t.compiled.call(this, vs, parent);
38127         } catch (e) {
38128             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
38129             Roo.log(e.toString());
38130             Roo.log(t.compiled);
38131             return '';
38132         }
38133     },
38134
38135     compileTpl : function(tpl)
38136     {
38137         var fm = Roo.util.Format;
38138         var useF = this.disableFormats !== true;
38139         var sep = Roo.isGecko ? "+" : ",";
38140         var undef = function(str) {
38141             Roo.log("Property not found :"  + str);
38142             return '';
38143         };
38144         
38145         var fn = function(m, name, format, args)
38146         {
38147             //Roo.log(arguments);
38148             args = args ? args.replace(/\\'/g,"'") : args;
38149             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
38150             if (typeof(format) == 'undefined') {
38151                 format= 'htmlEncode';
38152             }
38153             if (format == 'raw' ) {
38154                 format = false;
38155             }
38156             
38157             if(name.substr(0, 4) == 'xtpl'){
38158                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
38159             }
38160             
38161             // build an array of options to determine if value is undefined..
38162             
38163             // basically get 'xxxx.yyyy' then do
38164             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
38165             //    (function () { Roo.log("Property not found"); return ''; })() :
38166             //    ......
38167             
38168             var udef_ar = [];
38169             var lookfor = '';
38170             Roo.each(name.split('.'), function(st) {
38171                 lookfor += (lookfor.length ? '.': '') + st;
38172                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
38173             });
38174             
38175             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
38176             
38177             
38178             if(format && useF){
38179                 
38180                 args = args ? ',' + args : "";
38181                  
38182                 if(format.substr(0, 5) != "this."){
38183                     format = "fm." + format + '(';
38184                 }else{
38185                     format = 'this.call("'+ format.substr(5) + '", ';
38186                     args = ", values";
38187                 }
38188                 
38189                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
38190             }
38191              
38192             if (args.length) {
38193                 // called with xxyx.yuu:(test,test)
38194                 // change to ()
38195                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
38196             }
38197             // raw.. - :raw modifier..
38198             return "'"+ sep + udef_st  + name + ")"+sep+"'";
38199             
38200         };
38201         var body;
38202         // branched to use + in gecko and [].join() in others
38203         if(Roo.isGecko){
38204             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
38205                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
38206                     "';};};";
38207         }else{
38208             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
38209             body.push(tpl.body.replace(/(\r\n|\n)/g,
38210                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
38211             body.push("'].join('');};};");
38212             body = body.join('');
38213         }
38214         
38215         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
38216        
38217         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
38218         eval(body);
38219         
38220         return this;
38221     },
38222
38223     applyTemplate : function(values){
38224         return this.master.compiled.call(this, values, {});
38225         //var s = this.subs;
38226     },
38227
38228     apply : function(){
38229         return this.applyTemplate.apply(this, arguments);
38230     }
38231
38232  });
38233
38234 Roo.XTemplate.from = function(el){
38235     el = Roo.getDom(el);
38236     return new Roo.XTemplate(el.value || el.innerHTML);
38237 };